瀏覽代碼

fix bugs when dynamic array is passed as VAR argument

Vladislav Folts 10 年之前
父節點
當前提交
d5711fdd7b

二進制
bin/compiled.zip


+ 4 - 2
src/context.js

@@ -65,7 +65,8 @@ function checkTypeMatch(from, to){
 }
 
 function checkImplicitCast(types, from, to){
-    if (types.implicitCast(from, to, false, castOperations, {set: function(){}}))
+    var op;
+    if (types.implicitCast(from, to, false, castOperations, {set: function(v){op = v;}, get:function(){return op;}}))
         throwTypeMismatch(from, to);
 }
 
@@ -634,7 +635,8 @@ exports.ProcDecl = ChainedContext.extend({
         var result = this.__type.result();
         if (!result)
             throw new Errors.Error("unexpected RETURN in PROCEDURE declared with no result type");
-        if (this.language().types.implicitCast(type, result, false, castOperations, {set: function(){}}))
+        var op;
+        if (this.language().types.implicitCast(type, result, false, castOperations, {set: function(v){op = v;}, get:function(){return op;}}))
             throw new Errors.Error(
                 "RETURN '" + result.description() + "' expected, got '"
                 + type.description() + "'");

+ 43 - 6
src/eberon/EberonCast.ob

@@ -1,5 +1,5 @@
 MODULE EberonCast;
-IMPORT Cast, Code, EberonString, EberonDynamicArray, OberonRtl, Operator, Types;
+IMPORT Cast, Code, EberonString, EberonDynamicArray, OberonRtl, Types;
 TYPE
     CastOpToDynamicArray = RECORD (Cast.CastOp)
     END;
@@ -8,14 +8,45 @@ VAR
     castOpToDynamicArray: POINTER TO CastOpToDynamicArray;
 
 PROCEDURE CastOpToDynamicArray.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
-    RETURN Code.makeSimpleExpression(Operator.cloneArray(e.type()(Types.PArray)^, e.code(), rtl^), NIL)
+    RETURN Code.makeSimpleExpression(Cast.cloneArray(e.type()(Types.PArray)^, e.code(), rtl^), NIL)
 END CastOpToDynamicArray.make;
 
+PROCEDURE copyArray(t: Types.Array; leftCode, rightCode: STRING; rtl: OberonRtl.Type): STRING;
+VAR
+    result: STRING;
+BEGIN
+    IF Types.isScalar(Types.arrayElementsType(t)^) THEN
+        result := "Array.prototype.splice.apply(" + leftCode + ", [0, Number.MAX_VALUE].concat(" + rightCode + "))";
+    ELSIF Types.isScalar(Types.arrayBaseElementsType(t)^) THEN 
+        result := rtl.copyArrayOfScalars(rightCode, leftCode);
+    ELSE
+        result := rtl.copyArrayOfRecords(rightCode, leftCode);
+    END;
+    RETURN result
+END copyArray;
+
+PROCEDURE CastOpToDynamicArray.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
+    RETURN copyArray(left.type()(Types.PArray)^, left.code(), right.code(), rtl^)
+END CastOpToDynamicArray.assign;
+
 PROCEDURE isOpenCharArray(type: Types.PType): BOOLEAN;
     RETURN (type IS Types.POpenArray) 
          & (Types.arrayElementsType(type^) = Types.basic.ch)
 END isOpenCharArray;
 
+PROCEDURE areTypesExactlyMatch(t1: Types.PType; t2: Types.PType): BOOLEAN;
+VAR
+    result: BOOLEAN;
+BEGIN
+    IF (t1 IS EberonDynamicArray.PDynamicArray) & (t2 IS EberonDynamicArray.PDynamicArray) THEN
+        result := areTypesExactlyMatch(Types.arrayElementsType(t1^), 
+                                       Types.arrayElementsType(t2^));
+    ELSE
+        result := Cast.areTypesExactlyMatch(t1, t2);
+    END;
+    RETURN result
+END areTypesExactlyMatch;
+
 PROCEDURE implicit*(from, to: Types.PType; toVar: BOOLEAN; ops: Cast.Operations; VAR op: Cast.PCastOp): INTEGER;
 VAR
     result: INTEGER;
@@ -25,13 +56,19 @@ BEGIN
         IF toVar THEN 
             result := Cast.errVarParameter;
         ELSE
+            op := Cast.doNothing;
             result := Cast.errNo;
         END;
     ELSIF (from IS Types.PArray) & (to IS EberonDynamicArray.PDynamicArray)
-        & Cast.areTypesExactlyMatch(Types.arrayElementsType(from^), 
-                                    Types.arrayElementsType(to^)) THEN
-        IF toVar & ~(from IS EberonDynamicArray.PDynamicArray) THEN
-            result := Cast.errVarParameter;
+        & areTypesExactlyMatch(Types.arrayElementsType(from^), 
+                               Types.arrayElementsType(to^)) THEN
+        IF toVar THEN
+            IF ~(from IS EberonDynamicArray.PDynamicArray) THEN
+                result := Cast.errVarParameter;
+            ELSE
+                op := Cast.doNothing;
+                result := Cast.errNo;
+            END
         ELSE
             op := castOpToDynamicArray;
             result := Cast.errNo;

+ 2 - 2
src/eberon/EberonDynamicArray.ob

@@ -1,5 +1,5 @@
 MODULE EberonDynamicArray;
-IMPORT Code, Context, EberonTypes, Errors, LanguageContext, Procedure, Operator, Types;
+IMPORT Cast, Code, Context, EberonTypes, Errors, LanguageContext, Procedure, Types;
 CONST
     methodNameAdd = "add";
     methodNameClear = "clear";
@@ -172,7 +172,7 @@ BEGIN
     
     t <- e.type();
     IF t IS Types.PArray THEN
-        SELF.code := Operator.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);
     END;

+ 2 - 2
src/eberon/eberon_context.js

@@ -265,7 +265,7 @@ var InPlaceVariableInit = Context.Chained.extend({
         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 += op.cloneArray(type, e.code(), this.language().rtl);
+            this._code += Cast.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(op.cloneArray(type, e.code(), this.language().rtl), type);
+            e = Code.makeSimpleExpression(Cast.cloneArray(type, e.code(), this.language().rtl), type);
         Context.Return.prototype.handleExpression.call(this, e);
     }
 });

+ 78 - 25
src/ob/Cast.ob

@@ -1,21 +1,27 @@
 MODULE Cast;
-IMPORT Code, OberonRtl, Object, String, Types;
+IMPORT Code, OberonRtl, String, Types;
 CONST
     errNo* = 0;
     err* = 1;
     errVarParameter* = 2;
 TYPE
-    CastOp* = RECORD(Object.Type)
-        PROCEDURE make*(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression
+    CastOp* = RECORD
+        PROCEDURE make*(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
+        PROCEDURE assign*(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING
     END;
 
     PCastOp* = POINTER TO CastOp;
-(*
-    CastOpDoNothing = RECORD (CastOp)
+
+    CastOpDoNothing* = RECORD (CastOp)
+    END;
+
+    CastOpArray = RECORD (CastOpDoNothing)
+    END;
+
+    CastOpRecord = RECORD (CastOpDoNothing)
     END;
-*)
-    CastOpStrToChar = RECORD (CastOp)
-        c: CHAR
+
+    CastOpStrToChar = RECORD (CastOpDoNothing)
     END;
 
     Operations* = RECORD
@@ -25,12 +31,12 @@ TYPE
 VAR
     (*workaround recursive usage*)
     areTypesExactlyMatch*: PROCEDURE (t1: Types.PType; t2: Types.PType): BOOLEAN;
+
     doNothing*: POINTER TO CastOpDoNothing;
-(*
-PROCEDURE CastOpDoNothing.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
-    RETURN e
-END CastOpDoNothing.make;
-*)
+    castOpStrToChar: POINTER TO CastOpStrToChar;
+    castOpArray: POINTER TO CastOpArray;
+    castOpRecord: POINTER TO CastOpRecord;
+
 PROCEDURE findBaseType(base: Types.PRecord; type: Types.PRecord): Types.PRecord;
 BEGIN
     result <- type;
@@ -111,27 +117,65 @@ BEGIN
     RETURN result
 END areTypesExactlyMatchImpl;
 
-PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
-    RETURN Code.makeSimpleExpression(String.fromInt(ORD(SELF.c)), Types.basic.ch)
-END CastOpStrToChar.make;
+PROCEDURE CastOpDoNothing.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
+    RETURN e
+END CastOpDoNothing.make;
+
+PROCEDURE CastOpDoNothing.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
+BEGIN
+    rightCode <- SELF.make(rtl, Code.derefExpression(right)).code();
+    info <- left.designator().info();
+    IF (info IS Types.PVariable) & info.isReference() THEN
+        rightCode := ".set(" + rightCode + ")";
+    ELSE
+        rightCode := " = " + rightCode;
+    END;
+    RETURN left.lval() + rightCode
+END CastOpDoNothing.assign;
 
-PROCEDURE makeCastOpStrToChar(c: CHAR): PCastOp;
+PROCEDURE cloneArray*(t: Types.Array; code: STRING; rtl: OberonRtl.Type): STRING;
 VAR
-    result: POINTER TO CastOpStrToChar;
+    result: STRING;
 BEGIN
-    NEW(result);
-    result.c := c;
+    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 makeCastOpStrToChar;
+END cloneArray;
+
+PROCEDURE CastOpArray.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
+    RETURN left.code() + " = " + cloneArray(right.type()(Types.PArray)^, right.code(), rtl^)
+END CastOpArray.assign;
+
+PROCEDURE CastOpRecord.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
+    RETURN rtl.copyRecord(right.code(), left.code())
+END CastOpRecord.assign;
+
+PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
+BEGIN
+    s <- e.type()(Types.PString);
+    ASSERT(LEN(s.s) = 1);
+    c <- s.s[0];
+    code <- String.fromInt(ORD(c))
+    RETURN Code.makeSimpleExpression(code, Types.basic.ch)
+END CastOpStrToChar.make;
 
 PROCEDURE implicit*(from, to: Types.PType; toVar: BOOLEAN; ops: Operations; VAR op: PCastOp): INTEGER;
 VAR
-    c: CHAR;
     ignore: BOOLEAN;
 BEGIN
     result <- err;
     op := NIL;
     IF from = to THEN
+        IF from IS Types.PRecord THEN
+            op := castOpRecord;
+        ELSIF from IS Types.PArray THEN
+            op := castOpArray;
+        END;
         result := errNo;
     ELSIF (from = Types.basic.uint8) & (to = Types.basic.integer) THEN
         IF toVar THEN
@@ -148,8 +192,8 @@ BEGIN
         END;
     ELSIF from IS Types.PString THEN
         IF to = Types.basic.ch THEN
-            IF Types.stringAsChar(from^, c) THEN
-                op := makeCastOpStrToChar(c);
+            IF LEN(from.s) = 1 THEN
+                op := castOpStrToChar;
                 result := errNo;
             END;
         ELSIF Types.isString(to) THEN
@@ -163,6 +207,7 @@ BEGIN
         & (from.length() = to.length())
         & areTypesExactlyMatch(Types.arrayElementsType(from^), 
                                Types.arrayElementsType(to^)) THEN
+            op := castOpArray;
             result := errNo;
     ELSIF (from IS Types.PPointer) & (to IS Types.PPointer) THEN
         IF ~toVar THEN
@@ -176,6 +221,7 @@ BEGIN
         END;
     ELSIF (from IS Types.PRecord) & (to IS Types.PRecord) THEN
         IF findBaseType(to, from) # NIL THEN
+            op := castOpRecord;
             result := errNo;
         END;
     ELSIF (from = Types.nil) & matchesToNIL(to^) THEN
@@ -185,10 +231,17 @@ BEGIN
             result := errNo;
         END
     END;
+
+    IF (result = errNo) & (op = NIL) THEN
+        op := doNothing;
+    END;
     RETURN result
 END implicit;
 
 BEGIN
     areTypesExactlyMatch := areTypesExactlyMatchImpl;
-(*)    NEW(doNothing);*)
+    NEW(doNothing);
+    NEW(castOpArray);
+    NEW(castOpRecord);
+    NEW(castOpStrToChar);
 END Cast.

+ 2 - 0
src/ob/OberonRtl.ob

@@ -5,6 +5,8 @@ TYPE
         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;
         strCmp*: PROCEDURE(s1, s2: STRING): STRING;
         assignArrayFromString*: PROCEDURE(s1, s2: STRING): STRING;
         setInclL*: PROCEDURE(l, r: STRING): STRING;

+ 3 - 36
src/ob/Operator.ob

@@ -33,7 +33,7 @@ TYPE
         pred: CodePredicate
     END;
 
-    CastToUint8 = RECORD (Cast.CastOp)
+    CastToUint8 = RECORD (Cast.CastOpDoNothing)
     END;
 
 VAR
@@ -467,20 +467,6 @@ 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;
@@ -488,7 +474,6 @@ VAR
     leftCode, rightCode: STRING;
     isArray: BOOLEAN;
     castOperation: Cast.PCastOp;
-    castExp: Code.PExpression;
     ignored: BOOLEAN;
     result: STRING;
 
@@ -531,26 +516,8 @@ BEGIN
         IF (leftType IS Types.POpenArray) & (rightType IS Types.PArray) THEN
             Errors.raise("'" + leftCode + "' is open '" + leftType.description() + "' and cannot be assigned");
         END;
-        IF (castOperation = NIL) & (isArray OR (rightType IS Types.PRecord)) THEN
-            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));
-            ELSE
-                castExp := Code.derefExpression(right);
-            END;
-            rightCode := castExp.code();
-            IF (info IS Types.PVariable) & info(Types.PVariable).isReference() THEN
-                rightCode := ".set(" + rightCode + ")";
-            ELSE
-                rightCode := " = " + rightCode;
-            END;
-            result := leftCode + rightCode;
-        END;
+            
+        result := castOperation.assign(cx.rtl, left, right);
     END;
     RETURN result
 END assign;

+ 1 - 1
src/ob/Types.ob

@@ -76,7 +76,7 @@ TYPE
     PProcedureId* = POINTER TO ProcedureId;
 
     String* = RECORD(Type)
-        s: STRING
+        s-: STRING
     END;
 
     PString* = POINTER TO String;

+ 19 - 0
src/rtl.js

@@ -182,6 +182,23 @@ var impl = {
             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)){
@@ -211,6 +228,8 @@ exports.Class = Class;
 exports.dependencies = { 
     "cloneRecord": ["copyRecord"],
     "cloneArrayOfRecords": ["cloneRecord"],
+    "copyArrayOfRecords": ["cloneArrayOfRecords", "cloneRecord"],
+    "copyArrayOfScalars": ["cloneArrayOfScalars"],
     "makeCharArray": ["__makeCharArray"],
     "__makeCharArray": ["__setupCharArrayMethods"]
 };

+ 4 - 0
test/expected/array.js

@@ -114,6 +114,9 @@ function testAssign(){
 	aPointers2 = aPointers1.slice();
 	arrayOfArray2 = RTL$.cloneArrayOfScalars(arrayOfArray1);
 }
+
+function testPassOpenArray(a/*ARRAY OF INTEGER*/){
+}
 a1[0] = 1;
 a3[1] = true;
 a4[1][2] = true;
@@ -121,4 +124,5 @@ a4[1][2] = true;
 p1(a1);
 p2(a1);
 a1 = a11.slice();
+testPassOpenArray(a1);
 }();

+ 64 - 1
test/expected/eberon/dynamic_array.js

@@ -31,6 +31,30 @@ 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)
+            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();
@@ -50,6 +74,24 @@ var RTL$ = {
             }
         }
     },
+    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();
+
+        // 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;
+    },
     assert: function (condition){
         if (!condition)
             throw new Error("assertion failed");
@@ -76,12 +118,30 @@ var byte = 0;
 function assignDynamicArrayFromStatic(){
 	var static$ = RTL$.makeArray(3, 0);
 	var dynamic = [];
-	dynamic = static$.slice();
+	Array.prototype.splice.apply(dynamic, [0, Number.MAX_VALUE].concat(static$));
 }
 
 function returnOuterArray(){
 	return a.slice();
 }
+
+function passArrayBeRef(a/*VAR ARRAY * OF INTEGER*/){
+	var static$ = RTL$.makeArray(3, 0);
+	a[0] = 1;
+	a[0] = a[1];
+	Array.prototype.splice.apply(a, [0, Number.MAX_VALUE].concat(static$));
+	Array.prototype.splice.apply(a, [0, Number.MAX_VALUE].concat(dynamicInt));
+}
+
+function passArrayOfRecordsByRef(a/*VAR ARRAY * OF T*/){
+	var result = [];
+	RTL$.copyArrayOfRecords(result, a);
+}
+
+function passArrayOfArraysByRef(a/*VAR ARRAY *, 3 OF INTEGER*/){
+	var result = [];
+	RTL$.copyArrayOfScalars(result, a);
+}
 dynamicInt.push(3);
 dynamicInt.push(i);
 dynamicInt.push(byte);
@@ -97,4 +157,7 @@ RTL$.assert(dynamicInt.indexOf(i) != -1);
 RTL$.assert(dynamicChar.indexOf(34) != -1);
 dynamicInt.splice(i, 1);
 dynamicInt.splice(0, Number.MAX_VALUE);
+passArrayBeRef(dynamicInt);
+passArrayOfRecordsByRef(dynamicRecord);
+passArrayOfArraysByRef(dynamicArrayOfStaticArrayInt);
 }();

+ 4 - 0
test/expected/string.js

@@ -79,6 +79,10 @@ function p1(s/*ARRAY OF CHAR*/){
 
 function p2(c/*CHAR*/){
 }
+
+function charByRef(c/*VAR CHAR*/){
+	c.set(97);
+}
 ch1 = 34;
 RTL$.assignArrayFromString(a1, s1);
 RTL$.assignArrayFromString(a2, s2);

+ 6 - 1
test/input/array.ob

@@ -45,6 +45,9 @@ BEGIN
     arrayOfArray2 := arrayOfArray1;
 END testAssign;
 
+PROCEDURE testPassOpenArray(a: ARRAY OF INTEGER);
+END testPassOpenArray;
+
 BEGIN
     a1[0] := 1;
     a3[1] := TRUE;
@@ -54,5 +57,7 @@ BEGIN
     p1(a1);
     p2(a1);
 
-    a1 := a11
+    a1 := a11;
+
+    testPassOpenArray(a1);
 END m.

+ 29 - 0
test/input/eberon/dynamic_array.ob

@@ -31,6 +31,31 @@ PROCEDURE returnOuterArray(): DynamicInt;
     RETURN a
 END returnOuterArray;
 
+PROCEDURE passArrayBeRef(VAR a: ARRAY * OF INTEGER);
+VAR 
+    static: ARRAY 3 OF INTEGER;
+BEGIN
+    a[0] := 1;
+    a[0] := a[1];
+
+    a := static;
+    a := dynamicInt;
+END passArrayBeRef;
+
+PROCEDURE passArrayOfRecordsByRef(VAR a: ARRAY * OF T);
+VAR 
+    result: ARRAY * OF T;
+BEGIN
+    a := result;
+END passArrayOfRecordsByRef;
+
+PROCEDURE passArrayOfArraysByRef(VAR a: ARRAY * OF A);
+VAR 
+    result: ARRAY * OF A;
+BEGIN
+    a := result;
+END passArrayOfArraysByRef;
+
 BEGIN
     dynamicInt.add(3);
     dynamicInt.add(i);
@@ -54,4 +79,8 @@ BEGIN
 
     dynamicInt.remove(i);
     dynamicInt.clear();
+
+    passArrayBeRef(dynamicInt);
+    passArrayOfRecordsByRef(dynamicRecord);
+    passArrayOfArraysByRef(dynamicArrayOfStaticArrayInt);
 END m.

+ 66 - 0
test/input/eberon/run/dynamic_array.ob

@@ -48,6 +48,61 @@ BEGIN
     ASSERT(a[2] = 3);
 END testArrayReturn;
 
+PROCEDURE testCopyByVarArrayOfRecords();
+TYPE
+    T = RECORD
+        i: INTEGER
+    END;
+VAR
+    a: ARRAY * OF T;
+    r: T;
+
+    PROCEDURE test(VAR aRef: ARRAY * OF T);
+    VAR
+        local: ARRAY * OF T;
+    BEGIN
+        r.i := 123;
+        local.add(r);
+        aRef := local;
+    END test;
+
+BEGIN
+    ASSERT(LEN(a) = 0);
+    test(a);
+    ASSERT(LEN(a) = 1);
+    ASSERT(a[0].i = 123);
+    r.i := 345;
+    ASSERT(a[0].i = 123);
+END testCopyByVarArrayOfRecords;
+
+PROCEDURE testCopyByVarArrayOfArrays();
+VAR
+    a: ARRAY *,* OF INTEGER;
+    aInt: ARRAY * OF INTEGER;
+
+    PROCEDURE test(VAR aRef: ARRAY * OF ARRAY * OF INTEGER);
+    VAR
+        local: ARRAY *,* OF INTEGER;
+    BEGIN
+        local.add(aInt);
+        local[0].add(123);
+        aRef := local;
+    END test;
+
+BEGIN
+    ASSERT(LEN(a) = 0);
+    test(a);
+    ASSERT(LEN(a) = 1);
+    ASSERT(LEN(a[0]) = 1);
+    ASSERT(a[0][0] = 123);
+    ASSERT(LEN(aInt) = 0);
+END testCopyByVarArrayOfArrays;
+
+PROCEDURE testCopyByVar(a: ARRAY OF INTEGER; VAR result: ARRAY * OF INTEGER);
+BEGIN
+    result := a;
+END testCopyByVar;
+
 BEGIN
     static[0] := 1;
     static[1] := 2;
@@ -62,4 +117,15 @@ BEGIN
 
     testAddRemove();
     testArrayReturn();
+
+    dynamic.clear();
+    ASSERT(LEN(dynamic) = 0);
+    testCopyByVar(static, dynamic);
+    ASSERT(LEN(dynamic) = 3);
+    ASSERT(dynamic[0] = 1);
+    ASSERT(dynamic[1] = 2);
+    ASSERT(dynamic[2] = 3);
+
+    testCopyByVarArrayOfRecords();
+    testCopyByVarArrayOfArrays();
 END m.

+ 5 - 0
test/input/string.ob

@@ -22,6 +22,11 @@ END p1;
 PROCEDURE p2(c: CHAR);
 END p2;
 
+PROCEDURE charByRef(VAR c: CHAR);
+BEGIN
+	c := "a";
+END charByRef;
+
 BEGIN
     ch1 := s1;
 	a1 := s1;