Selaa lähdekoodia

fix record/array copy/clone - do not mangle record fields, use type info instead

Vladislav Folts 10 vuotta sitten
vanhempi
commit
0c9666dfca

BIN
bin/compiled.zip


+ 1 - 1
src/context.js

@@ -1893,7 +1893,7 @@ exports.ModuleDeclaration = ChainedContext.extend({
         else if (id === this.__name){
             var scope = parent.currentScope();
             scope.close();
-            var exports = scope.$exports;
+            var exports = scope.exports;
             Scope.defineExports(Scope.moduleSymbol(scope).info(), exports);
             this.codeGenerator().write(this.__moduleGen.epilog(exports));
         }

+ 7 - 9
src/eberon/EberonCast.ob

@@ -12,25 +12,23 @@ VAR
     castOpToMap: POINTER TO CastOpToMap;
 
 PROCEDURE CastOpToDynamicArray.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
-    RETURN Code.makeSimpleExpression(Cast.cloneArray(e.type()(Types.PArray)^, e.code(), rtl^), NIL)
-END CastOpToDynamicArray.make;
+    RETURN Code.makeSimpleExpression(Cast.cloneArray(e.type()(Types.PArray), e.code(), rtl^), NIL)
+END;
 
-PROCEDURE copyArray(t: Types.Array; leftCode, rightCode: STRING; rtl: OberonRtl.Type): STRING;
+PROCEDURE copyArray(t: Types.PArray; leftCode, rightCode: STRING; rtl: OberonRtl.Type): STRING;
 VAR
     result: STRING;
 BEGIN
-    IF Types.arrayElementsType(t).isScalar() THEN
+    IF t.elementsType.isScalar() THEN
         result := "Array.prototype.splice.apply(" + leftCode + ", [0, Number.MAX_VALUE].concat(" + rightCode + "))";
-    ELSIF Types.arrayBaseElementsType(t).isScalar() THEN 
-        result := rtl.copyArrayOfScalars(rightCode, leftCode);
     ELSE
-        result := rtl.copyArrayOfRecords(rightCode, leftCode);
+        result := rtl.copy(rightCode, leftCode, Types.generateTypeInfo(t));
     END;
     RETURN result
-END copyArray;
+END;
 
 PROCEDURE CastOpToDynamicArray.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
-    RETURN copyArray(left.type()(Types.PArray)^, left.code(), right.code(), rtl^)
+    RETURN copyArray(left.type()(Types.PArray), left.code(), right.code(), rtl^)
 END;
 
 PROCEDURE CastOpToMap.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;

+ 2 - 2
src/eberon/EberonDynamicArray.ob

@@ -121,9 +121,9 @@ BEGIN
     
     t <- e.type();
     IF t IS Types.PArray THEN
-        SELF.code := Cast.cloneArray(t^, SELF.code, SELF.cx.rtl^);
+        SELF.code := Cast.cloneArray(t, SELF.code, SELF.cx.rtl^);
     ELSIF t IS Types.PRecord THEN
-        SELF.code := SELF.cx.rtl.cloneRecord(SELF.code);
+        SELF.code := SELF.cx.rtl.clone(SELF.code, Types.generateTypeInfo(t));
     END;
 END AddCallGenerator.handleArgument;
 

+ 1 - 1
src/eberon/eberon_context.js

@@ -357,7 +357,7 @@ var InPlaceVariableInit = Context.Chained.extend({
         if (type instanceof Type.Record){
             EberonRecord.ensureCanBeInstantiated(this, type, EberonRecord.instantiateForCopy);
             if (e.designator())
-                this._code += this.language().rtl.cloneRecord(e.code());
+                this._code += this.language().rtl.clone(e.code(), Type.generateTypeInfo(type));
             else // do not clone if it is temporary, e.g. constructor call
                 this._code += e.code();
         }

+ 6 - 8
src/ob/Cast.ob

@@ -149,30 +149,28 @@ PROCEDURE CastOpDoNothing.clone(rtl: OberonRtl.PType; e: Code.PExpression): STRI
     RETURN Code.derefExpression(e).code();
 END;
 
-PROCEDURE cloneArray*(t: Types.Array; code: STRING; rtl: OberonRtl.Type): STRING;
+PROCEDURE cloneArray*(t: Types.PArray; code: STRING; rtl: OberonRtl.Type): STRING;
 VAR
     result: STRING;
 BEGIN
-    IF Types.arrayElementsType(t).isScalar() THEN
+    IF Types.arrayElementsType(t^).isScalar() THEN
         result := code + ".slice()";
-    ELSIF Types.arrayBaseElementsType(t).isScalar() THEN 
-        result := rtl.cloneArrayOfScalars(code);
     ELSE
-        result := rtl.cloneArrayOfRecords(code);
+        result := rtl.clone(code, Types.generateTypeInfo(t));
     END;
     RETURN result
 END;
 
 PROCEDURE CastOpArray.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
-    RETURN left.code() + " = " + cloneArray(right.type()(Types.PArray)^, right.code(), rtl^)
+    RETURN left.code() + " = " + cloneArray(right.type()(Types.PArray), right.code(), rtl^)
 END;
 
 PROCEDURE CastOpArray.clone(rtl: OberonRtl.PType; e: Code.PExpression): STRING;
-    RETURN cloneArray(e.type()(Types.PArray)^, e.code(), rtl^);
+    RETURN cloneArray(e.type()(Types.PArray), e.code(), rtl^);
 END;
 
 PROCEDURE CastOpRecord.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
-    RETURN rtl.copyRecord(right.code(), left.code())
+    RETURN rtl.copy(right.code(), left.code(), Types.generateTypeInfo(left.type()))
 END;
 
 PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;

+ 2 - 6
src/ob/OberonRtl.ob

@@ -1,12 +1,8 @@
 MODULE OberonRtl;
 TYPE
     Type* = RECORD
-        cloneRecord*: PROCEDURE(s: STRING): STRING;
-        copyRecord*: PROCEDURE(s1, s2: STRING): STRING;
-        cloneArrayOfRecords*: PROCEDURE(s: STRING): STRING;
-        cloneArrayOfScalars*: PROCEDURE(s: STRING): STRING;
-        copyArrayOfRecords*: PROCEDURE(left, right: STRING): STRING;
-        copyArrayOfScalars*: PROCEDURE(left, right: STRING): STRING;
+        copy*: PROCEDURE(from, to, type: STRING): STRING;
+        clone*: PROCEDURE(from, type: STRING): STRING;
         strCmp*: PROCEDURE(s1, s2: STRING): STRING;
         assignArrayFromString*: PROCEDURE(s1, s2: STRING): STRING;
         setInclL*: PROCEDURE(l, r: STRING): STRING;

+ 35 - 21
src/ob/Types.ob

@@ -236,6 +236,8 @@ VAR
     numeric*: ARRAY * OF PType;
     nil*: POINTER TO Nil;
 
+    pGenerateTypeInfo: PROCEDURE(type: PType): STRING;
+
 PROCEDURE TypeId.description(): STRING;
 VAR
     t: PType;
@@ -632,17 +634,6 @@ PROCEDURE arrayElementsType*(a: Array): PStorageType;
     RETURN a.elementsType
 END arrayElementsType;
 
-PROCEDURE arrayBaseElementsType*(a: Array): PStorageType;
-VAR
-    result: PStorageType;
-BEGIN
-    result := a.elementsType;
-    WHILE result IS PArray DO
-        result := result(PArray).elementsType;
-    END;
-    RETURN result;
-END;
-
 PROCEDURE StaticArray.length(): INTEGER;
     RETURN SELF.len
 END StaticArray.length;
@@ -770,16 +761,7 @@ BEGIN
 END;
 
 PROCEDURE mangleField*(id: STRING; type: PStorageType): STRING;
-BEGIN
-    result <- id;
-    IF type.isScalar() 
-        OR ((type IS PArray)
-            & arrayBaseElementsType(type^).isScalar()) THEN
-        result := mangleJSProperty(result);
-    ELSE
-        result := "$" + result;
-    END;
-    RETURN result;
+    RETURN mangleJSProperty(id);
 END;
 
 PROCEDURE RecordField.id(): STRING;
@@ -815,6 +797,36 @@ PROCEDURE RecordField.RecordField(identdef: Context.PIdentdefInfo; type: PStorag
       mType(type);
 END;
 
+PROCEDURE dumpRecordFields*(type: PRecord): STRING;
+VAR
+    result: STRING;
+BEGIN
+    IF type.base # NIL THEN
+        result := dumpRecordFields(type.base);
+    END;
+    FOREACH v, k IN type.fields DO
+        IF LEN(result) # 0 THEN
+            result := result + ", ";
+        END;
+        result := result + k + ": " + pGenerateTypeInfo(v.type());
+    END;
+    RETURN result;
+END;
+
+PROCEDURE generateTypeInfo*(type: PType): STRING;
+VAR
+    result: STRING;
+BEGIN
+    IF type IS PRecord THEN
+        result := "{record: {" + dumpRecordFields(type) + "}}";
+    ELSIF type IS PArray THEN
+        result := "{array: " + generateTypeInfo(type.elementsType) + "}";
+    ELSE
+        result := "null";
+    END;
+    RETURN result;
+END;
+
 BEGIN
     basic.bool := NEW BasicType("BOOLEAN", "false");
     basic.ch := NEW BasicType("CHAR", "0");
@@ -828,4 +840,6 @@ BEGIN
     numeric.add(basic.real);
 
     NEW(nil);
+
+    pGenerateTypeInfo := generateTypeInfo;
 END Types.

+ 50 - 60
src/rtl.js

@@ -169,66 +169,58 @@ var methods = {
         }
         return cmp ? cmp : s1.length - s2.length;
     },
-    cloneArrayOfRecords: function(from){
-        var length = from.length;
-        var result = new Array(length);
-        if (length){
-            var method = from[0] instanceof Array 
-                       ? this.cloneArrayOfRecords // this is array of arrays, go deeper
-                       : this.cloneRecord;
-            for(var i = 0; i < result.length; ++i)
-                result[i] = method.call(this, from[i]);
-        }
-        return result;
-    },
-    cloneArrayOfScalars: function(from){
-        var length = from.length;
-        if (!length)
-            return [];
-        if (!(from[0] instanceof Array))
-            return from.slice();
-
-        // this is array of arrays, go deeper
-        var result = new Array(length);
-        for(var i = 0; i < length; ++i)
-            result[i] = this.cloneArrayOfScalars(from[i]);
-        return result;
-    },
-    copyArrayOfRecords: function(from, to){
-        to.splice(0, to.length);
-        var length = from.length;
-        if (!length)
-            return;
-
-        var method = from[0] instanceof Array 
-                   ? this.cloneArrayOfRecords // this is array of arrays, go deeper
-                   : this.cloneRecord;
-        for(var i = 0; i < length; ++i)
-            to.push(method.call(this, from[i]));
-    },
-    copyArrayOfScalars: function(from, to){
-        to.splice(0, to.length);
-        for(var i = 0; i < from.length; ++i)
-            to.push(this.cloneArrayOfScalars(from[i]));
-    },
-    copyRecord: function(from, to){
-        for(var prop in to){
-            if (to.hasOwnProperty(prop)){
-                var v = from[prop];
-                var isScalar = prop[0] != "$";
-                if (isScalar)
-                    to[prop] = v;
+    copy: function(from, to, type){
+        var r = type.record;
+        if (r){
+            for(var f in r){
+                var fieldType = r[f];
+                if (fieldType){
+                    // temporary support for mangled fields
+                    var mangled = "$" + f;
+                    if (!from.hasOwnProperty(mangled))
+                        mangled = f;
+                    this.copy(from[mangled], to[mangled], fieldType);
+                }
                 else
-                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
-                                                  : this.cloneRecord(v);
+                    to[f] = from[f];
+            }
+            return;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow copy
+                Array.prototype.splice.apply(to, [0, to.length].concat(from));
+            else {
+                // deep copy
+                to.splice(0, to.length);
+                for(var i = 0; i < from.length; ++i)
+                    to.push(this.clone(from[i], a));
             }
         }
     },
-    cloneRecord: function(from){
-        var Ctr = from.constructor;
-        var result = new Ctr();
-        this.copyRecord(from, result);
-        return result;
+    clone: function(from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
+
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
+        }
     },
     assert: function(condition){
         if (!condition)
@@ -239,10 +231,8 @@ var methods = {
 exports.Class = Class;
 exports.rtl = {
     dependencies: { 
-        "cloneRecord": ["copyRecord"],
-        "cloneArrayOfRecords": ["cloneRecord"],
-        "copyArrayOfRecords": ["cloneArrayOfRecords", "cloneRecord"],
-        "copyArrayOfScalars": ["cloneArrayOfScalars"],
+        "copy": ["clone"],
+        "clone": ["copy"],
         "makeCharArray": ["__makeCharArray"],
         "__makeCharArray": ["__setupCharArrayMethods"]
     },

+ 4 - 2
src/rtl_code.js

@@ -50,8 +50,10 @@ var rtlPrototype = {
         return result;
     },
     __putEntry: function(name){
-        if (!this.__entries[name])
-            this.__entries[name] = this.__rtl.methods[name];
+        if (this.__entries[name])
+            return;
+        
+        this.__entries[name] = this.__rtl.methods[name];
         
         var dependencies = this.__rtl.dependencies[name];
         if (dependencies)

+ 49 - 40
test/expected/array.js

@@ -17,49 +17,58 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
-    cloneArrayOfRecords: function (from){
-        var length = from.length;
-        var result = new Array(length);
-        if (length){
-            var method = from[0] instanceof Array 
-                       ? this.cloneArrayOfRecords // this is array of arrays, go deeper
-                       : this.cloneRecord;
-            for(var i = 0; i < result.length; ++i)
-                result[i] = method.call(this, from[i]);
+    clone: function (from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
+
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
         }
-        return result;
-    },
-    cloneRecord: function (from){
-        var Ctr = from.constructor;
-        var result = new Ctr();
-        this.copyRecord(from, result);
-        return result;
     },
-    copyRecord: function (from, to){
-        for(var prop in to){
-            if (to.hasOwnProperty(prop)){
-                var v = from[prop];
-                var isScalar = prop[0] != "$";
-                if (isScalar)
-                    to[prop] = v;
+    copy: function (from, to, type){
+        var r = type.record;
+        if (r){
+            for(var f in r){
+                var fieldType = r[f];
+                if (fieldType){
+                    // temporary support for mangled fields
+                    var mangled = "$" + f;
+                    if (!from.hasOwnProperty(mangled))
+                        mangled = f;
+                    this.copy(from[mangled], to[mangled], fieldType);
+                }
                 else
-                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
-                                                  : this.cloneRecord(v);
+                    to[f] = from[f];
+            }
+            return;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow copy
+                Array.prototype.splice.apply(to, [0, to.length].concat(from));
+            else {
+                // deep copy
+                to.splice(0, to.length);
+                for(var i = 0; i < from.length; ++i)
+                    to.push(this.clone(from[i], a));
             }
         }
-    },
-    cloneArrayOfScalars: function (from){
-        var length = from.length;
-        if (!length)
-            return [];
-        if (!(from[0] instanceof Array))
-            return from.slice();
-
-        // this is array of arrays, go deeper
-        var result = new Array(length);
-        for(var i = 0; i < length; ++i)
-            result[i] = this.cloneArrayOfScalars(from[i]);
-        return result;
     }
 };
 var m = function (){
@@ -92,9 +101,9 @@ function testAssign(){
 	var aPointers1 = RTL$.makeArray(3, null);var aPointers2 = RTL$.makeArray(3, null);
 	var arrayOfArray1 = RTL$.makeArray(3, 5, false);var arrayOfArray2 = RTL$.makeArray(3, 5, false);
 	aInts2 = aInts1.slice();
-	aRecords2 = RTL$.cloneArrayOfRecords(aRecords1);
+	aRecords2 = RTL$.clone(aRecords1, {array: {record: {}}});
 	aPointers2 = aPointers1.slice();
-	arrayOfArray2 = RTL$.cloneArrayOfScalars(arrayOfArray1);
+	arrayOfArray2 = RTL$.clone(arrayOfArray1, {array: {array: null}});
 }
 
 function testPassOpenArray(a/*ARRAY OF INTEGER*/){

+ 3 - 3
test/expected/eberon/constructor.js

@@ -18,8 +18,8 @@ RTL$.extend(DerivedRecordWithParamConstructorWithoutConstructor, RecordWithParam
 function MixAutoAndManualInitFields(){
 	this.iAuto = 0;
 	this.iManual = 123;
-	this.$rAuto = new T();
-	this.$rManual = new RecordWithParamConstructor(345);
+	this.rAuto = new T();
+	this.rManual = new RecordWithParamConstructor(345);
 	this.setManual = 8;
 	this.stringAuto = '';
 }
@@ -57,7 +57,7 @@ function InitializeField(){
 	this.i = 123;
 }
 function InitializeRecordField(){
-	this.$r = new RecordWithParamConstructor(123);
+	this.r = new RecordWithParamConstructor(123);
 }
 function InitializeMangledField(){
 	this.constructor$ = 123;

+ 49 - 57
test/expected/eberon/dynamic_array.js

@@ -17,66 +17,58 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
-    copyArrayOfRecords: function (from, to){
-        to.splice(0, to.length);
-        var length = from.length;
-        if (!length)
+    copy: function (from, to, type){
+        var r = type.record;
+        if (r){
+            for(var f in r){
+                var fieldType = r[f];
+                if (fieldType){
+                    // temporary support for mangled fields
+                    var mangled = "$" + f;
+                    if (!from.hasOwnProperty(mangled))
+                        mangled = f;
+                    this.copy(from[mangled], to[mangled], fieldType);
+                }
+                else
+                    to[f] = from[f];
+            }
             return;
-
-        var method = from[0] instanceof Array 
-                   ? this.cloneArrayOfRecords // this is array of arrays, go deeper
-                   : this.cloneRecord;
-        for(var i = 0; i < length; ++i)
-            to.push(method.call(this, from[i]));
-    },
-    cloneArrayOfRecords: function (from){
-        var length = from.length;
-        var result = new Array(length);
-        if (length){
-            var method = from[0] instanceof Array 
-                       ? this.cloneArrayOfRecords // this is array of arrays, go deeper
-                       : this.cloneRecord;
-            for(var i = 0; i < result.length; ++i)
-                result[i] = method.call(this, from[i]);
         }
-        return result;
-    },
-    cloneRecord: function (from){
-        var Ctr = from.constructor;
-        var result = new Ctr();
-        this.copyRecord(from, result);
-        return result;
-    },
-    copyRecord: function (from, to){
-        for(var prop in to){
-            if (to.hasOwnProperty(prop)){
-                var v = from[prop];
-                var isScalar = prop[0] != "$";
-                if (isScalar)
-                    to[prop] = v;
-                else
-                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
-                                                  : this.cloneRecord(v);
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow copy
+                Array.prototype.splice.apply(to, [0, to.length].concat(from));
+            else {
+                // deep copy
+                to.splice(0, to.length);
+                for(var i = 0; i < from.length; ++i)
+                    to.push(this.clone(from[i], a));
             }
         }
     },
-    copyArrayOfScalars: function (from, to){
-        to.splice(0, to.length);
-        for(var i = 0; i < from.length; ++i)
-            to.push(this.cloneArrayOfScalars(from[i]));
-    },
-    cloneArrayOfScalars: function (from){
-        var length = from.length;
-        if (!length)
-            return [];
-        if (!(from[0] instanceof Array))
-            return from.slice();
+    clone: function (from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
 
-        // this is array of arrays, go deeper
-        var result = new Array(length);
-        for(var i = 0; i < length; ++i)
-            result[i] = this.cloneArrayOfScalars(from[i]);
-        return result;
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
+        }
     },
     assert: function (condition){
         if (!condition)
@@ -119,12 +111,12 @@ function passArrayBeRef(a/*VAR ARRAY * OF INTEGER*/){
 
 function passArrayOfRecordsByRef(a/*VAR ARRAY * OF T*/){
 	var result = [];
-	RTL$.copyArrayOfRecords(result, a);
+	RTL$.copy(result, a, {array: {record: {a: {array: null}}}});
 }
 
 function passArrayOfArraysByRef(a/*VAR ARRAY *, 3 OF INTEGER*/){
 	var result = [];
-	RTL$.copyArrayOfScalars(result, a);
+	RTL$.copy(result, a, {array: {array: null}});
 }
 dynamicInt.push(3);
 dynamicInt.push(i);
@@ -135,7 +127,7 @@ dynamicString.push(s);
 dynamicChar.push(34);
 dynamicByte.push(byte);
 dynamicByte.push(i & 0xFF);
-dynamicRecord.push(RTL$.cloneRecord(r));
+dynamicRecord.push(RTL$.clone(r, {record: {a: {array: null}}}));
 dynamicArrayOfStaticArrayInt.push(a.slice());
 RTL$.assert(dynamicInt.indexOf(i) != -1);
 RTL$.assert(dynamicChar.indexOf(34) != -1);

+ 51 - 17
test/expected/eberon/in_place_variables.js

@@ -23,22 +23,56 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
-    cloneRecord: function (from){
-        var Ctr = from.constructor;
-        var result = new Ctr();
-        this.copyRecord(from, result);
-        return result;
+    clone: function (from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
+
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
+        }
     },
-    copyRecord: function (from, to){
-        for(var prop in to){
-            if (to.hasOwnProperty(prop)){
-                var v = from[prop];
-                var isScalar = prop[0] != "$";
-                if (isScalar)
-                    to[prop] = v;
+    copy: function (from, to, type){
+        var r = type.record;
+        if (r){
+            for(var f in r){
+                var fieldType = r[f];
+                if (fieldType){
+                    // temporary support for mangled fields
+                    var mangled = "$" + f;
+                    if (!from.hasOwnProperty(mangled))
+                        mangled = f;
+                    this.copy(from[mangled], to[mangled], fieldType);
+                }
                 else
-                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
-                                                  : this.cloneRecord(v);
+                    to[f] = from[f];
+            }
+            return;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow copy
+                Array.prototype.splice.apply(to, [0, to.length].concat(from));
+            else {
+                // deep copy
+                to.splice(0, to.length);
+                for(var i = 0; i < from.length; ++i)
+                    to.push(this.clone(from[i], a));
             }
         }
     },
@@ -69,13 +103,13 @@ function void$(){
 }
 
 function valueArgs(r/*Derived*/, i/*INTEGER*/, a/*ARRAY 10 OF INTEGER*/){
-	var v1 = RTL$.cloneRecord(r);
+	var v1 = RTL$.clone(r, {record: {derivedField: null}});
 	var v2 = i;
 	var v3 = a.slice();
 }
 
 function varArgs(r/*VAR Derived*/, i/*VAR INTEGER*/, a/*ARRAY 10 OF INTEGER*/){
-	var v1 = RTL$.cloneRecord(r);
+	var v1 = RTL$.clone(r, {record: {derivedField: null}});
 	var v2 = i.get();
 	var v3 = a.slice();
 }
@@ -102,7 +136,7 @@ var v6 = i + i | 0;
 var v7 = p();
 var v8 = void$;
 var do$ = 0;
-var tempRecord = RTL$.cloneRecord(r);
+var tempRecord = RTL$.clone(r, {record: {derivedField: null}});
 var tempArray = a.slice();
 pdVar = new Derived();
 pbVar = pdVar;

+ 53 - 13
test/expected/eberon/map.js

@@ -82,29 +82,69 @@ var RTL$ = {
             throw new Error("invalid key: " + key);
         return map[key];
     },
-    copyRecord: function (from, to){
-        for(var prop in to){
-            if (to.hasOwnProperty(prop)){
-                var v = from[prop];
-                var isScalar = prop[0] != "$";
-                if (isScalar)
-                    to[prop] = v;
+    copy: function (from, to, type){
+        var r = type.record;
+        if (r){
+            for(var f in r){
+                var fieldType = r[f];
+                if (fieldType){
+                    // temporary support for mangled fields
+                    var mangled = "$" + f;
+                    if (!from.hasOwnProperty(mangled))
+                        mangled = f;
+                    this.copy(from[mangled], to[mangled], fieldType);
+                }
                 else
-                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
-                                                  : this.cloneRecord(v);
+                    to[f] = from[f];
             }
+            return;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow copy
+                Array.prototype.splice.apply(to, [0, to.length].concat(from));
+            else {
+                // deep copy
+                to.splice(0, to.length);
+                for(var i = 0; i < from.length; ++i)
+                    to.push(this.clone(from[i], a));
+            }
+        }
+    },
+    clone: function (from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
+
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
         }
     }
 };
 var test = function (){
 var m = {};
 function anonymous$1(){
-	this.$m = {};
+	this.m = {};
 }
 var r = new anonymous$1();
 var a = RTL$.makeArray(1, {});
 function RecordWithMapInitializedInConstructor(m/*MAP OF INTEGER*/){
-	this.$m = RTL$.cloneMapOfScalars(m);
+	this.m = RTL$.cloneMapOfScalars(m);
 }
 
 function ForEach(){
@@ -176,7 +216,7 @@ function put(){
 	m[a] = 4;
 	RTL$.getMappedValue(mapOfMap, "abc")["cde"] = 5;
 	RTL$.getMappedValue(mapOfRecord, "abc").field = 6;
-	RTL$.copyRecord(new T(), RTL$.getMappedValue(mapOfPointer, "abc"));
+	RTL$.copy(new T(), RTL$.getMappedValue(mapOfPointer, "abc"), {record: {field: null}});
 }
 
 function in$(){
@@ -236,6 +276,6 @@ for(var k in $map1){
 	}
 }
 passByRef(m);
-passByRef(r.$m);
+passByRef(r.m);
 passByRef(a[0]);
 }();

+ 60 - 18
test/expected/record.js

@@ -23,17 +23,57 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
-    copyRecord: function (from, to){
-        for(var prop in to){
-            if (to.hasOwnProperty(prop)){
-                var v = from[prop];
-                var isScalar = prop[0] != "$";
-                if (isScalar)
-                    to[prop] = v;
+    copy: function (from, to, type){
+        var r = type.record;
+        if (r){
+            for(var f in r){
+                var fieldType = r[f];
+                if (fieldType){
+                    // temporary support for mangled fields
+                    var mangled = "$" + f;
+                    if (!from.hasOwnProperty(mangled))
+                        mangled = f;
+                    this.copy(from[mangled], to[mangled], fieldType);
+                }
                 else
-                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
-                                                  : this.cloneRecord(v);
+                    to[f] = from[f];
             }
+            return;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow copy
+                Array.prototype.splice.apply(to, [0, to.length].concat(from));
+            else {
+                // deep copy
+                to.splice(0, to.length);
+                for(var i = 0; i < from.length; ++i)
+                    to.push(this.clone(from[i], a));
+            }
+        }
+    },
+    clone: function (from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
+
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
         }
     },
     makeRef: function (obj, prop){
@@ -54,11 +94,11 @@ function T1(){
 }
 RTL$.extend(T1, Base1);
 function RecordWithInnerRecord(){
-	this.$r = new T1();
+	this.r = new T1();
 }
 function RecordWithInnerArray(){
 	this.aInts = RTL$.makeArray(3, 0);
-	this.$aRecords = RTL$.makeArray(3, function(){return new T1();});
+	this.aRecords = RTL$.makeArray(3, function(){return new T1();});
 	this.aPointers = RTL$.makeArray(3, null);
 }
 function RecordWithMangledFields(){
@@ -66,7 +106,7 @@ function RecordWithMangledFields(){
 	this.prototype$ = false;
 }
 var b1 = new Base1();
-var r1 = new T1();
+var r1 = new T1();var r2 = new T1();
 var recordWithInnerRecord = new RecordWithInnerRecord();
 var recordWithInnerArray = new RecordWithInnerArray();
 var recordWithMangledFields = new RecordWithMangledFields();
@@ -80,15 +120,17 @@ function p2(r/*VAR T1*/){
 
 function byRef(i/*VAR INTEGER*/){
 }
-RTL$.copyRecord(r1, b1);
+RTL$.copy(r1, b1, {record: {}});
+RTL$.copy(r2, r1, {record: {i: null}});
+RTL$.copy(recordWithInnerArray, recordWithInnerArray, {record: {aInts: {array: null}, aRecords: {array: {record: {i: null}}}, aPointers: {array: null}}});
 p1(r1);
 p2(r1);
-recordWithInnerRecord.$r.i = 123;
-p1(recordWithInnerRecord.$r);
-p2(recordWithInnerRecord.$r);
-byRef(RTL$.makeRef(recordWithInnerRecord.$r, "i"));
+recordWithInnerRecord.r.i = 123;
+p1(recordWithInnerRecord.r);
+p2(recordWithInnerRecord.r);
+byRef(RTL$.makeRef(recordWithInnerRecord.r, "i"));
 recordWithInnerArray.aInts[0] = 123;
-recordWithInnerArray.$aRecords[0].i = 123;
+recordWithInnerArray.aRecords[0].i = 123;
 recordWithInnerArray.aPointers[0].i = 123;
 RTL$.assert(recordWithMangledFields.constructor$ == 0);
 RTL$.assert(!recordWithMangledFields.prototype$);

+ 3 - 1
test/input/record.ob

@@ -19,7 +19,7 @@ TYPE
     END;
 VAR
 	b1: Base1;
-	r1: T1;
+	r1, r2: T1;
     recordWithInnerRecord: RecordWithInnerRecord;
     recordWithInnerArray: RecordWithInnerArray;
     recordWithMangledFields: RecordWithMangledFields;
@@ -37,6 +37,8 @@ END byRef;
 
 BEGIN
 	b1 := r1;
+    r1 := r2;
+    recordWithInnerArray := recordWithInnerArray;
     p1(r1);
     p2(r1);
 

+ 54 - 0
test/input/run/copy.ob

@@ -11,6 +11,12 @@ TYPE
 		p: POINTER TO T1
 	END;
 
+	Arrays = RECORD
+		aIntegers: ARRAY 3 OF INTEGER;
+		aRecords: ARRAY 5 OF T1;
+		aChars: ARRAY 7 OF ARRAY 10 OF CHAR
+	END;
+
 VAR
 	a1: ARRAY 3 OF T1;
 	a2: ARRAY 3 OF T1;
@@ -31,6 +37,52 @@ BEGIN
 	ASSERT(r1.p = r2.p);
 END testPointerAndRecordFieldsCopy;
 
+PROCEDURE testRecordArraysCopy();
+VAR
+	r1, r2: Arrays;
+BEGIN
+	r2.aIntegers[0] := 1;
+	r2.aIntegers[1] := 2;
+	r2.aIntegers[2] := 3;
+	r2.aRecords[0].i1 := 1;
+	r2.aChars[1][2] := "a";
+	r1 := r2;
+	ASSERT(r1.aIntegers[0] = 1);
+	ASSERT(r1.aIntegers[1] = 2);
+	ASSERT(r1.aIntegers[2] = 3);
+	ASSERT(r1.aRecords[0].i1 = 1);
+	ASSERT(r1.aChars[1][2] = "a");
+
+	r2.aIntegers[1] := 5;
+	ASSERT(r1.aIntegers[1] = 2);
+
+	r2.aRecords[0].i1 := 3;
+	ASSERT(r1.aRecords[0].i1 = 1);
+
+	r2.aChars[1][2] := "b";
+	ASSERT(r1.aChars[1][2] = "a");
+END testRecordArraysCopy;
+
+PROCEDURE testBaseCopy();
+TYPE
+	Base = RECORD END;
+	Derived = RECORD(Base) field: INTEGER END;
+VAR
+	r1, r2: Derived;
+
+	PROCEDURE copy(r1: Base; VAR r2: Base);
+	BEGIN
+		r2 := r1;
+	END copy;
+
+BEGIN
+	r1.field := 1;
+	r2.field := 2;
+	copy(r1, r2);
+	ASSERT(r1.field = 1);
+	ASSERT(r2.field = 2);
+END testBaseCopy;
+
 BEGIN
 	a1[0].i1 := 123;
 	a1[0].proc1 := proc1;
@@ -46,4 +98,6 @@ BEGIN
 	ASSERT(a2[0].proc1 = proc1);
 
 	testPointerAndRecordFieldsCopy();
+	testRecordArraysCopy();
+	testBaseCopy();
 END m.