Kaynağa Gözat

fix arrays/records copying/cloning

Vladislav Folts 10 yıl önce
ebeveyn
işleme
fa021c1ccf

BIN
bin/compiled.zip


+ 19 - 8
src/context.js

@@ -297,6 +297,15 @@ function castCode(type, context){
     return context.qualifyScope(Type.recordScope(baseType)) + Type.recordConstructor(baseType);
 }
 
+function mangleField(id, type){
+    if (Type.isScalar(type) 
+        || (type instanceof Type.Array 
+            && Type.isScalar(Type.arrayBaseElementsType(type))))
+        return id;
+
+    return "$" + id;
+}
+
 exports.Designator = ChainedContext.extend({
     init: function Context$Designator(context){
         ChainedContext.prototype.init.call(this, context);
@@ -341,14 +350,14 @@ exports.Designator = ChainedContext.extend({
             isReadOnly = false;
         }
         var field = t.denote(id);
-        this.__derefCode = this.__code;
-        this.__propCode = "\"" + id + "\"";
-        this.__info = field.asVar(isReadOnly, this);
         this.__currentType = field.type();
-        var code = this.__currentType instanceof Type.Procedure 
+        this.__derefCode = this.__code;
+        var codeId = this.__currentType instanceof Type.Procedure 
                  ? this.__currentType.designatorCode(id)
-                 : id;
-        this.__code += "." + code;
+                 : mangleField(id, this.__currentType);
+        this.__propCode = "\"" + codeId + "\"";
+        this.__info = field.asVar(isReadOnly, this);
+        this.__code += "." + codeId;
         this.__scope = undefined;
     },
     _makeDerefVar: function(){
@@ -1760,8 +1769,10 @@ exports.RecordDecl = ChainedContext.extend({
         if (baseType)
             gen.write(qualifiedBase + ".prototype.init.call(this);\n");
         var ownFields = Type.recordOwnFields(type);
-        for(var f in ownFields)
-            gen.write("this." + f + " = " + ownFields[f].type().initializer(this) + ";\n");
+        for(var f in ownFields){
+            var fieldType = ownFields[f].type();
+            gen.write("this." + mangleField(f, fieldType) + " = " + fieldType.initializer(this) + ";\n");
+        }
 
         gen.closeScope("");
         gen.closeScope(");\n");

+ 2 - 2
src/eberon/EberonCast.ob

@@ -1,5 +1,5 @@
 MODULE EberonCast;
-IMPORT Cast, Code, EberonString, EberonDynamicArray, OberonRtl, Types;
+IMPORT Cast, Code, EberonString, EberonDynamicArray, OberonRtl, Operator, Types;
 TYPE
     CastOpToDynamicArray = RECORD (Cast.CastOp)
     END;
@@ -8,7 +8,7 @@ VAR
     castOpToDynamicArray: POINTER TO CastOpToDynamicArray;
 
 PROCEDURE CastOpToDynamicArray.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
-    RETURN Code.makeSimpleExpression(rtl.clone(e.code()), NIL)
+    RETURN Code.makeSimpleExpression(Operator.cloneArray(e.type()(Types.PArray)^, e.code(), rtl^), NIL)
 END CastOpToDynamicArray.make;
 
 PROCEDURE isOpenCharArray(type: Types.PType): BOOLEAN;

+ 5 - 3
src/eberon/EberonDynamicArray.ob

@@ -1,5 +1,5 @@
 MODULE EberonDynamicArray;
-IMPORT Code, Context, EberonTypes, Errors, JsArray, LanguageContext, Procedure, Types;
+IMPORT Code, Context, EberonTypes, Errors, JsArray, LanguageContext, Procedure, Operator, Types;
 CONST
     methodNameAdd = "add";
     methodNameClear = "clear";
@@ -171,8 +171,10 @@ BEGIN
     SELF.code := argCode.result();
     
     t <- e.type();
-    IF (t IS Types.PRecord) OR (t IS Types.PArray) THEN
-        SELF.code := SELF.cx.rtl.clone(SELF.code);
+    IF t IS Types.PArray THEN
+        SELF.code := Operator.cloneArray(t^, SELF.code, SELF.cx.rtl^);
+    ELSIF t IS Types.PRecord THEN
+        SELF.code := SELF.cx.rtl.cloneRecord(SELF.code);
     END;
 END AddCallGenerator.handleArgument;
 

+ 3 - 3
src/eberon/eberon_context.js

@@ -260,12 +260,12 @@ var InPlaceVariableInit = Context.Chained.extend({
         this._symbol = Symbol.makeSymbol(this.__id, v);
         if (type instanceof Type.Record){
             type.initializer(this, false); // checks for abstract etc.
-            this._code += this.language().rtl.clone(e.code());
+            this._code += this.language().rtl.cloneRecord(e.code());
         }
         else if (type instanceof Type.Array){
             if (type instanceof Type.OpenArray)
                 throw new Errors.Error("cannot initialize variable '" + this.__id + "' with open array");
-            this._code += this.language().rtl.clone(e.code());
+            this._code += op.cloneArray(type, e.code(), this.language().rtl);
         }
         else
             this._code += Code.derefExpression(e).code();
@@ -1094,7 +1094,7 @@ var Return = Context.Return.extend({
     handleExpression: function(e){
         var type = e.type();
         if (type instanceof Type.Array)
-            e = Code.makeSimpleExpression(this.language().rtl.clone(e.code()), type);
+            e = Code.makeSimpleExpression(op.cloneArray(type, e.code(), this.language().rtl), type);
         Context.Return.prototype.handleExpression.call(this, e);
     }
 });

+ 4 - 2
src/ob/OberonRtl.ob

@@ -1,8 +1,10 @@
 MODULE OberonRtl;
 TYPE
     Type* = RECORD
-        clone*: PROCEDURE(s: STRING): STRING;
-        copy*: PROCEDURE(s1, s2: STRING): STRING;
+        cloneRecord*: PROCEDURE(s: STRING): STRING;
+        copyRecord*: PROCEDURE(s1, s2: STRING): STRING;
+        cloneArrayOfRecords*: PROCEDURE(s: STRING): STRING;
+        cloneArrayOfScalars*: PROCEDURE(s: STRING): STRING;
         strCmp*: PROCEDURE(s1, s2: STRING): STRING;
         assignArrayFromString*: PROCEDURE(s1, s2: STRING): STRING;
         setInclL*: PROCEDURE(l, r: STRING): STRING;

+ 19 - 1
src/ob/Operator.ob

@@ -467,6 +467,20 @@ BEGIN
             Types.basic.bool)
 END strCmp;
 
+PROCEDURE cloneArray*(t: Types.Array; code: STRING; rtl: OberonRtl.Type): STRING;
+VAR
+    result: STRING;
+BEGIN
+    IF Types.isScalar(Types.arrayElementsType(t)^) THEN
+        result := code + ".slice()";
+    ELSIF Types.isScalar(Types.arrayBaseElementsType(t)^) THEN 
+        result := rtl.cloneArrayOfScalars(code);
+    ELSE
+        result := rtl.cloneArrayOfRecords(code);
+    END;
+    RETURN result
+END cloneArray;
+
 PROCEDURE assign*(left, right: Code.PExpression; cx: LanguageContext.Type): STRING;
 VAR
     designator: Code.PDesignator;
@@ -518,7 +532,11 @@ BEGIN
             Errors.raise("'" + leftCode + "' is open '" + leftType.description() + "' and cannot be assigned");
         END;
         IF (castOperation = NIL) & (isArray OR (rightType IS Types.PRecord)) THEN
-            result := cx.rtl.copy(rightCode, leftCode);
+            IF rightType IS Types.PArray THEN
+                result := leftCode + " = " + cloneArray(rightType^, rightCode, cx.rtl^);
+            ELSE
+                result := cx.rtl.copyRecord(rightCode, leftCode);
+            END;
         ELSE
             IF castOperation # NIL THEN
                 castExp := castOperation.make(cx.rtl, Code.derefExpression(right));

+ 15 - 0
src/ob/Types.ob

@@ -561,6 +561,21 @@ PROCEDURE arrayElementsType*(a: Array): PType;
     RETURN a.elementsType
 END arrayElementsType;
 
+PROCEDURE arrayBaseElementsType*(a: Array): PType;
+VAR
+    result: PType;
+BEGIN
+    result := a.elementsType;
+    WHILE result IS PArray DO
+        result := result(PArray).elementsType;
+    END;
+    RETURN result
+END arrayBaseElementsType;
+
+PROCEDURE isScalar*(VAR t: Type): BOOLEAN;
+    RETURN ~(t IS Array) & ~(t IS Record) 
+END isScalar;
+
 PROCEDURE StaticArray.length(): INTEGER;
     RETURN SELF.len
 END StaticArray.length;

+ 50 - 37
src/rtl.js

@@ -68,9 +68,18 @@ var impl = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
+    __setupCharArrayMethods: function(a){
+        var rtl = this;
+        a.charCodeAt = function(i){return this[i];};
+        a.slice = function(){
+            var result = Array.prototype.slice.apply(this, arguments);
+            rtl.__setupCharArrayMethods(result);
+            return result;
+        };
+    },
     __makeCharArray: function(length){
         var result = new Uint16Array(length);
-        result.charCodeAt = function(i){return this[i];};
+        this.__setupCharArrayMethods(result);
         return result;
     },
     makeCharArray: function(/*dimensions*/){
@@ -95,7 +104,7 @@ var impl = {
             return result;
         }
 
-        forward.push(this.__makeCharArray.bind(undefined, length));
+        forward.push(this.__makeCharArray.bind(this, length));
         return makeArray.apply(undefined, forward);
     },
     makeSet: function(/*...*/){
@@ -148,47 +157,49 @@ var impl = {
         }
         return cmp ? cmp : s1.length - s2.length;
     },
-    copy: function(from, to){
+    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;
+    },
+    copyRecord: function(from, to){
         for(var prop in to){
             if (to.hasOwnProperty(prop)){
                 var v = from[prop];
-                if (v !== null && typeof v == "object")
-                    this.copy(v, to[prop]);
-                else
+                var isScalar = prop[0] != "$";
+                if (isScalar)
                     to[prop] = v;
+                else
+                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
+                                                  : this.cloneRecord(v);
             }
         }
     },
-    clone: function(from){
-        var to;
-        var len;
-        var i;
+    cloneRecord: function(from){
         var Ctr = from.constructor;
-        if (Ctr == Uint16Array){
-            len = from.length;
-            to = this.__makeCharArray(len);
-            for(i = 0; i < len; ++i)
-                to[i] = from[i];
-        }
-        else {
-            to = new Ctr();
-            if (Ctr == Array)
-                len = from.length;
-                if (len){
-                    if (typeof from[0] != "object")
-                        for(i = 0; i < len; ++i)
-                            to[i] = from[i];
-                    else
-                        for(i = 0; i < len; ++i){
-                            var o = from[i];
-                            if (o !== null)
-                                to[i] = this.clone(o);
-                        }
-                }
-            else
-                this.copy(from, to);
-        }
-        return to;
+        var result = new Ctr();
+        this.copyRecord(from, result);
+        return result;
     },
     assert: function(condition){
         if (!condition)
@@ -198,8 +209,10 @@ var impl = {
 
 exports.Class = Class;
 exports.dependencies = { 
-    "clone": ["copy", "__makeCharArray"] ,
-    "makeCharArray": ["__makeCharArray"]
+    "cloneRecord": ["copyRecord"],
+    "cloneArrayOfRecords": ["cloneRecord"],
+    "makeCharArray": ["__makeCharArray"],
+    "__makeCharArray": ["__setupCharArrayMethods"]
 };
 
 for(var e in impl)

+ 10 - 11
src/rtl_code.js

@@ -49,21 +49,20 @@ exports.RTL = Rtl.Class.extend({
         
         return result;
     },
+    __putEntry: function(name){
+        if (!this.__entries[name])
+            this.__entries[name] = Rtl[name];
+        
+        var dependencies = Rtl.dependencies[name];
+        if (dependencies)
+            for(var i = 0; i < dependencies.length; ++i)
+                this.__putEntry(dependencies[i]);
+    },
     __makeIdOnDemand: function(name){
         return function(){
             if (this.__demandedCallback)
                 this.__demandedCallback();
-            if (!this.__entries[name])
-                this.__entries[name] = Rtl[name];
-            
-            var dependencies = Rtl.dependencies[name];
-            if (dependencies)
-                for(var i = 0; i < dependencies.length; ++i){
-                    var d = dependencies[i];
-                    if (!this.__entries[d])
-                        this.__entries[d] = Rtl[d];
-                }
-
+            this.__putEntry(name);
             return this.name() + "." + name;
         };
     },

+ 53 - 5
test/expected/array.js

@@ -31,16 +31,49 @@ var RTL$ = {
         result.extend = extend;
         return result;
     },
-    copy: function (from, to){
+    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];
-                if (v !== null && typeof v == "object")
-                    this.copy(v, to[prop]);
-                else
+                var isScalar = prop[0] != "$";
+                if (isScalar)
                     to[prop] = v;
+                else
+                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
+                                                  : this.cloneRecord(v);
             }
         }
+    },
+    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 (){
@@ -66,11 +99,26 @@ function p1(a/*ARRAY 10 OF INTEGER*/){
 function p2(a/*VAR ARRAY 10 OF INTEGER*/){
 	p1(a);
 }
+
+function testAssign(){
+	var T = RTL$.extend({
+		init: function T(){
+		}
+	});
+	var aInts1 = RTL$.makeArray(3, 0);var aInts2 = RTL$.makeArray(3, 0);
+	var aRecords1 = RTL$.makeArray(3, function(){return new T();});var aRecords2 = RTL$.makeArray(3, function(){return new T();});
+	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);
+	aPointers2 = aPointers1.slice();
+	arrayOfArray2 = RTL$.cloneArrayOfScalars(arrayOfArray1);
+}
 a1[0] = 1;
 a3[1] = true;
 a4[1][2] = true;
 a4[1][2] = true;
 p1(a1);
 p2(a1);
-RTL$.copy(a11, a1);
+a1 = a11.slice();
 }();

+ 11 - 2
test/expected/blur.js

@@ -21,13 +21,22 @@ var RTL$ = {
             return result;
         }
 
-        forward.push(this.__makeCharArray.bind(undefined, length));
+        forward.push(this.__makeCharArray.bind(this, length));
         return makeArray.apply(undefined, forward);
     },
     __makeCharArray: function (length){
         var result = new Uint16Array(length);
-        result.charCodeAt = function(i){return this[i];};
+        this.__setupCharArrayMethods(result);
         return result;
+    },
+    __setupCharArrayMethods: function (a){
+        var rtl = this;
+        a.charCodeAt = function(i){return this[i];};
+        a.slice = function(){
+            var result = Array.prototype.slice.apply(this, arguments);
+            rtl.__setupCharArrayMethods(result);
+            return result;
+        };
     }
 };
 var Blur = function (){

+ 14 - 42
test/expected/eberon/dynamic_array.js

@@ -31,53 +31,25 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
-    clone: function (from){
-        var to;
-        var len;
-        var i;
+    cloneRecord: function (from){
         var Ctr = from.constructor;
-        if (Ctr == Uint16Array){
-            len = from.length;
-            to = this.__makeCharArray(len);
-            for(i = 0; i < len; ++i)
-                to[i] = from[i];
-        }
-        else {
-            to = new Ctr();
-            if (Ctr == Array)
-                len = from.length;
-                if (len){
-                    if (typeof from[0] != "object")
-                        for(i = 0; i < len; ++i)
-                            to[i] = from[i];
-                    else
-                        for(i = 0; i < len; ++i){
-                            var o = from[i];
-                            if (o !== null)
-                                to[i] = this.clone(o);
-                        }
-                }
-            else
-                this.copy(from, to);
-        }
-        return to;
+        var result = new Ctr();
+        this.copyRecord(from, result);
+        return result;
     },
-    copy: function (from, to){
+    copyRecord: function (from, to){
         for(var prop in to){
             if (to.hasOwnProperty(prop)){
                 var v = from[prop];
-                if (v !== null && typeof v == "object")
-                    this.copy(v, to[prop]);
-                else
+                var isScalar = prop[0] != "$";
+                if (isScalar)
                     to[prop] = v;
+                else
+                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
+                                                  : this.cloneRecord(v);
             }
         }
     },
-    __makeCharArray: function (length){
-        var result = new Uint16Array(length);
-        result.charCodeAt = function(i){return this[i];};
-        return result;
-    },
     assert: function (condition){
         if (!condition)
             throw new Error("assertion failed");
@@ -104,11 +76,11 @@ var byte = 0;
 function assignDynamicArrayFromStatic(){
 	var static$ = RTL$.makeArray(3, 0);
 	var dynamic = [];
-	dynamic = RTL$.clone(static$);
+	dynamic = static$.slice();
 }
 
 function returnOuterArray(){
-	return RTL$.clone(a);
+	return a.slice();
 }
 dynamicInt.push(3);
 dynamicInt.push(i);
@@ -119,8 +91,8 @@ dynamicString.push(s);
 dynamicChar.push(34);
 dynamicByte.push(byte);
 dynamicByte.push(i & 0xFF);
-dynamicRecord.push(RTL$.clone(r));
-dynamicArrayOfStaticArrayInt.push(RTL$.clone(a));
+dynamicRecord.push(RTL$.cloneRecord(r));
+dynamicArrayOfStaticArrayInt.push(a.slice());
 RTL$.assert(dynamicInt.indexOf(i) != -1);
 RTL$.assert(dynamicChar.indexOf(34) != -1);
 dynamicInt.splice(i, 1);

+ 16 - 44
test/expected/eberon/in_place_variables.js

@@ -31,53 +31,25 @@ var RTL$ = {
                 result[i] = this.makeArray.apply(this, forward);
         return result;
     },
-    clone: function (from){
-        var to;
-        var len;
-        var i;
+    cloneRecord: function (from){
         var Ctr = from.constructor;
-        if (Ctr == Uint16Array){
-            len = from.length;
-            to = this.__makeCharArray(len);
-            for(i = 0; i < len; ++i)
-                to[i] = from[i];
-        }
-        else {
-            to = new Ctr();
-            if (Ctr == Array)
-                len = from.length;
-                if (len){
-                    if (typeof from[0] != "object")
-                        for(i = 0; i < len; ++i)
-                            to[i] = from[i];
-                    else
-                        for(i = 0; i < len; ++i){
-                            var o = from[i];
-                            if (o !== null)
-                                to[i] = this.clone(o);
-                        }
-                }
-            else
-                this.copy(from, to);
-        }
-        return to;
+        var result = new Ctr();
+        this.copyRecord(from, result);
+        return result;
     },
-    copy: function (from, to){
+    copyRecord: function (from, to){
         for(var prop in to){
             if (to.hasOwnProperty(prop)){
                 var v = from[prop];
-                if (v !== null && typeof v == "object")
-                    this.copy(v, to[prop]);
-                else
+                var isScalar = prop[0] != "$";
+                if (isScalar)
                     to[prop] = v;
+                else
+                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
+                                                  : this.cloneRecord(v);
             }
         }
     },
-    __makeCharArray: function (length){
-        var result = new Uint16Array(length);
-        result.charCodeAt = function(i){return this[i];};
-        return result;
-    },
     assert: function (condition){
         if (!condition)
             throw new Error("assertion failed");
@@ -108,15 +80,15 @@ function void$(){
 }
 
 function valueArgs(r/*Derived*/, i/*INTEGER*/, a/*ARRAY 10 OF INTEGER*/){
-	var v1 = RTL$.clone(r);
+	var v1 = RTL$.cloneRecord(r);
 	var v2 = i;
-	var v3 = RTL$.clone(a);
+	var v3 = a.slice();
 }
 
 function varArgs(r/*VAR Derived*/, i/*VAR INTEGER*/, a/*ARRAY 10 OF INTEGER*/){
-	var v1 = RTL$.clone(r);
+	var v1 = RTL$.cloneRecord(r);
 	var v2 = i.get();
-	var v3 = RTL$.clone(a);
+	var v3 = a.slice();
 }
 
 function pChar(c/*CHAR*/){
@@ -141,8 +113,8 @@ var v6 = i + i | 0;
 var v7 = p();
 var v8 = void$;
 var do$ = 0;
-var tempRecord = RTL$.clone(r);
-var tempArray = RTL$.clone(a);
+var tempRecord = RTL$.cloneRecord(r);
+var tempArray = a.slice();
 pdVar = new Derived();
 pbVar = pdVar;
 var pb = pbVar;

+ 11 - 2
test/expected/len.js

@@ -39,14 +39,23 @@ var RTL$ = {
             return result;
         }
 
-        forward.push(this.__makeCharArray.bind(undefined, length));
+        forward.push(this.__makeCharArray.bind(this, length));
         return makeArray.apply(undefined, forward);
     },
     __makeCharArray: function (length){
         var result = new Uint16Array(length);
-        result.charCodeAt = function(i){return this[i];};
+        this.__setupCharArrayMethods(result);
         return result;
     },
+    __setupCharArrayMethods: function (a){
+        var rtl = this;
+        a.charCodeAt = function(i){return this[i];};
+        a.slice = function(){
+            var result = Array.prototype.slice.apply(this, arguments);
+            rtl.__setupCharArrayMethods(result);
+            return result;
+        };
+    },
     assert: function (condition){
         if (!condition)
             throw new Error("assertion failed");

+ 54 - 5
test/expected/record.js

@@ -13,16 +13,40 @@ var RTL$ = {
         result.extend = extend;
         return result;
     },
-    copy: function (from, to){
+    makeArray: function (/*dimensions, initializer*/){
+        var forward = Array.prototype.slice.call(arguments);
+        var result = new Array(forward.shift());
+        var i;
+        if (forward.length == 1){
+            var init = forward[0];
+            if (typeof init == "function")
+                for(i = 0; i < result.length; ++i)
+                    result[i] = init();
+            else
+                for(i = 0; i < result.length; ++i)
+                    result[i] = init;
+        }
+        else
+            for(i = 0; i < result.length; ++i)
+                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];
-                if (v !== null && typeof v == "object")
-                    this.copy(v, to[prop]);
-                else
+                var isScalar = prop[0] != "$";
+                if (isScalar)
                     to[prop] = v;
+                else
+                    to[prop] = v instanceof Array ? this.cloneArrayOfRecords(v)
+                                                  : this.cloneRecord(v);
             }
         }
+    },
+    makeRef: function (obj, prop){
+        return {set: function(v){ obj[prop] = v; },
+                get: function(){ return obj[prop]; }};
     }
 };
 var m = function (){
@@ -33,10 +57,25 @@ var Base1 = RTL$.extend({
 var T1 = Base1.extend({
 	init: function T1(){
 		Base1.prototype.init.call(this);
+		this.i = 0;
+	}
+});
+var RecordWithInnerRecord = RTL$.extend({
+	init: function RecordWithInnerRecord(){
+		this.$r = new T1();
+	}
+});
+var RecordWithInnerArray = RTL$.extend({
+	init: function RecordWithInnerArray(){
+		this.aInts = RTL$.makeArray(3, 0);
+		this.$aRecords = RTL$.makeArray(3, function(){return new T1();});
+		this.aPointers = RTL$.makeArray(3, null);
 	}
 });
 var b1 = new Base1();
 var r1 = new T1();
+var recordWithInnerRecord = new RecordWithInnerRecord();
+var recordWithInnerArray = new RecordWithInnerArray();
 
 function p1(r/*T1*/){
 }
@@ -44,7 +83,17 @@ function p1(r/*T1*/){
 function p2(r/*VAR T1*/){
 	p1(r);
 }
-RTL$.copy(r1, b1);
+
+function byRef(i/*VAR INTEGER*/){
+}
+RTL$.copyRecord(r1, b1);
 p1(r1);
 p2(r1);
+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.aPointers[0].i = 123;
 }();

+ 11 - 2
test/expected/string.js

@@ -21,14 +21,23 @@ var RTL$ = {
             return result;
         }
 
-        forward.push(this.__makeCharArray.bind(undefined, length));
+        forward.push(this.__makeCharArray.bind(this, length));
         return makeArray.apply(undefined, forward);
     },
     __makeCharArray: function (length){
         var result = new Uint16Array(length);
-        result.charCodeAt = function(i){return this[i];};
+        this.__setupCharArrayMethods(result);
         return result;
     },
+    __setupCharArrayMethods: function (a){
+        var rtl = this;
+        a.charCodeAt = function(i){return this[i];};
+        a.slice = function(){
+            var result = Array.prototype.slice.apply(this, arguments);
+            rtl.__setupCharArrayMethods(result);
+            return result;
+        };
+    },
     assignArrayFromString: function (a, s){
         var i;
         for(i = 0; i < s.length; ++i)

+ 16 - 0
test/input/array.ob

@@ -29,6 +29,22 @@ BEGIN
     p1(a)
 END p2;
 
+PROCEDURE testAssign();
+TYPE
+    T = RECORD
+    END;
+VAR
+    aInts1, aInts2: ARRAY 3 OF INTEGER;
+    aRecords1, aRecords2: ARRAY 3 OF T;
+    aPointers1, aPointers2: ARRAY 3 OF POINTER TO T;
+    arrayOfArray1, arrayOfArray2: ARRAY 3, 5 OF BOOLEAN;
+BEGIN
+    aInts2 := aInts1;
+    aRecords2 := aRecords1;
+    aPointers2 := aPointers1;
+    arrayOfArray2 := arrayOfArray1;
+END testAssign;
+
 BEGIN
     a1[0] := 1;
     a3[1] := TRUE;

+ 26 - 2
test/input/record.ob

@@ -1,10 +1,22 @@
 MODULE m;
 TYPE
 	Base1 = RECORD END;
-	T1 = RECORD (Base1) END;
+	T1 = RECORD (Base1) 
+        i: INTEGER
+    END;
+    RecordWithInnerRecord = RECORD
+        r: T1
+    END;
+    RecordWithInnerArray = RECORD
+        aInts: ARRAY 3 OF INTEGER;
+        aRecords: ARRAY 3 OF T1;
+        aPointers: ARRAY 3 OF POINTER TO T1
+    END;
 VAR
 	b1: Base1;
 	r1: T1;
+    recordWithInnerRecord: RecordWithInnerRecord;
+    recordWithInnerArray: RecordWithInnerArray;
 
 PROCEDURE p1(r: T1);
 END p1;
@@ -14,8 +26,20 @@ BEGIN
     p1(r)
 END p2;
 
+PROCEDURE byRef(VAR i: INTEGER);
+END byRef;
+
 BEGIN
 	b1 := r1;
     p1(r1);
-    p2(r1)
+    p2(r1);
+
+    recordWithInnerRecord.r.i := 123;
+    p1(recordWithInnerRecord.r);
+    p2(recordWithInnerRecord.r);
+    byRef(recordWithInnerRecord.r.i);
+
+    recordWithInnerArray.aInts[0] := 123;
+    recordWithInnerArray.aRecords[0].i := 123;
+    recordWithInnerArray.aPointers[0].i := 123;
 END m.

+ 8 - 6
test/input/run/array.ob

@@ -1,11 +1,13 @@
 MODULE m;
-
+TYPE
+    T = RECORD
+    END;
 VAR
-    a1: ARRAY 5, 10 OF INTEGER;
+    aIntsX2: ARRAY 5, 10 OF INTEGER;
 
 BEGIN
-    a1[0, 1] := 1;
-    ASSERT(a1[0, 1] = 1);
-    a1[1, 0] := a1[0, 1];
-    ASSERT(a1[1, 0] = 1);
+    aIntsX2[0, 1] := 1;
+    ASSERT(aIntsX2[0, 1] = 1);
+    aIntsX2[1, 0] := aIntsX2[0, 1];
+    ASSERT(aIntsX2[1, 0] = 1);
 END m.

+ 18 - 1
test/input/run/copy.ob

@@ -6,6 +6,11 @@ TYPE
 		proc1: PROCEDURE
 	END;
 
+	MixPointersAndRecords = RECORD
+		r: T1;
+		p: POINTER TO T1
+	END;
+
 VAR
 	a1: ARRAY 3 OF T1;
 	a2: ARRAY 3 OF T1;
@@ -16,6 +21,16 @@ END proc1;
 PROCEDURE proc2;
 END proc2;
 
+PROCEDURE testPointerAndRecordFieldsCopy();
+VAR
+	r1, r2: MixPointersAndRecords;
+BEGIN
+	NEW(r1.p);
+	NEW(r2.p);
+	r1 := r2;
+	ASSERT(r1.p = r2.p);
+END testPointerAndRecordFieldsCopy;
+
 BEGIN
 	a1[0].i1 := 123;
 	a1[0].proc1 := proc1;
@@ -28,5 +43,7 @@ BEGIN
 	a1[0].i1 := 345;
 	a1[0].proc1 := proc2;
 	ASSERT(a2[0].i1 = 123);
-	ASSERT(a2[0].proc1 = proc1)
+	ASSERT(a2[0].proc1 = proc1);
+
+	testPointerAndRecordFieldsCopy();
 END m.