Skip to content

Commit aa0cd13

Browse files
committed
[js] Partial implementation of multi-dim arrays.
Support throwing errors from internals by throw new NQPException("...") instead of always using a ctx.
1 parent 51d350b commit aa0cd13

File tree

6 files changed

+202
-27
lines changed

6 files changed

+202
-27
lines changed

src/vm/js/QAST/Compiler.nqp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -786,13 +786,18 @@ class QAST::OperationsJS {
786786
add_simple_op('pop' ~ $suffix, $type, [$T_OBJ], sub ($array) {"$array.pop()"}, :sideffects);
787787
add_simple_op('push' ~ $suffix, $type, [$T_OBJ, $type], sub ($array, $elem) {"$array.push($elem)"}, :sideffects);
788788

789-
add_simple_op('atposnd' ~ $suffix, $type, [$T_OBJ, $T_OBJ], :ctx);
790-
add_simple_op('bindposnd' ~ $suffix, $type, [$T_OBJ, $T_OBJ, $type], :ctx, :sideffects);
789+
add_simple_op('atposnd' ~ $suffix, $type, [$T_OBJ, $T_OBJ]);
790+
add_simple_op('atpos2d' ~ $suffix, $type, [$T_OBJ, $T_INT, $T_INT, $type], :sideffects);
791+
add_simple_op('atpos3d' ~ $suffix, $type, [$T_OBJ, $T_INT, $T_INT, $T_INT, $type], :sideffects);
792+
793+
add_simple_op('bindposnd' ~ $suffix, $type, [$T_OBJ, $T_OBJ, $type], :sideffects);
794+
add_simple_op('bindpos2d' ~ $suffix, $type, [$T_OBJ, $T_INT, $T_INT, $type], :sideffects);
795+
add_simple_op('bindpos3d' ~ $suffix, $type, [$T_OBJ, $T_INT, $T_INT, $T_INT, $type], :sideffects);
791796
}
792797

793798
add_simple_op('numdimensions', $T_INT, [$T_OBJ]);
794799
add_simple_op('dimensions', $T_OBJ, [$T_OBJ]);
795-
add_simple_op('setdimensions', $T_OBJ, [$T_OBJ, $T_OBJ], :sideffects, :ctx);
800+
add_simple_op('setdimensions', $T_OBJ, [$T_OBJ, $T_OBJ], :sideffects);
796801

797802
add_op('hash', sub ($comp, $node, :$want, :$cps) {
798803
# TODO CPS
@@ -1004,10 +1009,11 @@ class QAST::OperationsJS {
10041009
"try \{",
10051010
$body,
10061011
# HACK we need to check $body.type if we handle something like return
1007-
"//want: $want, body_expr: <{$body.expr}>, body_type:<{$body.type}>\n",
10081012
(($want == $T_VOID || $body.type == $T_VOID) ?? '' !! "$try_ret = {$body.expr};\n"),
10091013
"\} catch (e) \{if (e === $unwind_marker) \{",
10101014
($want == $T_VOID ?? '' !! "$try_ret = $unwind_marker.ret;\n"),
1015+
"\} else if (e instanceof nqp.NQPException) \{\n",
1016+
"$*CTX.catchException(e);\n",
10111017
"\} else \{\n",
10121018
"throw e;\n",
10131019
"\}\n",
@@ -1075,7 +1081,7 @@ class QAST::OperationsJS {
10751081

10761082
add_simple_op('splice', $T_OBJ, [$T_OBJ, $T_OBJ, $T_INT, $T_INT], :sideffects);
10771083

1078-
add_simple_op('setelems', $T_OBJ, [$T_OBJ, $T_INT], :sideffects, sub ($array, $elems) {"($array.length = $elems, $array)"});
1084+
add_simple_op('setelems', $T_OBJ, [$T_OBJ, $T_INT], :sideffects);
10791085

10801086

10811087
add_simple_op('iterator', $T_OBJ, [$T_OBJ], :sideffects);

src/vm/js/nqp-runtime/core.js

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ var CodeRef = require('./code-ref.js');
77
var LexPadHack = require('./lexpad-hack.js');
88
var NQPInt = require('./nqp-int.js');
99

10+
var NQPException = require('./nqp-exception.js');
11+
1012
var reprs = require('./reprs.js');
1113

1214
exports.CodeRef = CodeRef;
@@ -502,11 +504,21 @@ op.unbox_s = function(obj) {
502504
op.elems = function(obj) {
503505
if (obj instanceof Array) {
504506
return obj.length;
505-
} else if (obj.$$elems) {
507+
} else {
506508
return obj.$$elems();
507509
}
508510
};
509511

512+
op.setelems = function(obj, elems) {
513+
if (obj instanceof Array) {
514+
obj.length = elems;
515+
return obj;
516+
} else {
517+
obj.$$setelems(elems);
518+
return obj;
519+
}
520+
};
521+
510522
op.markcodestatic = function(code) {
511523
code.isStatic = true;
512524
return code;
@@ -874,48 +886,77 @@ op.getcodecuid = function(codeRef) {
874886
op.numdimensions = function(array) {
875887
if (array instanceof Array) {
876888
return 1;
889+
} else {
890+
return array.$$numdimensions();
877891
}
878892
};
879893

880894
op.dimensions = function(array) {
881895
if (array instanceof Array) {
882896
return [array.length];
897+
} else {
898+
return array.$$dimensions();
883899
}
884900
};
885901

886-
op.setdimensions = function(ctx, array, dimensions) {
902+
op.setdimensions = function(array, dimensions) {
887903
if (array instanceof Array) {
888904
if (dimensions.length != 1) {
889-
ctx.die("A dynamic array can only have a single dimension");
905+
throw new NQPException("A dynamic array can only have a single dimension");
890906
} else {
891-
//console.log("setting ", array, dimensions);
892907
array.length = dimensions[0];
893908
}
909+
} else {
910+
array.$$setdimensions(dimensions);
894911
}
912+
return dimensions;
895913
};
896914

897-
op.atposnd = function(ctx, array, idx) {
915+
op.atposnd = function(array, idx) {
898916
if (array instanceof Array) {
899917
if (idx.length != 1) {
900-
ctx.die("A dynamic array can only be indexed with a single dimension");
918+
throw new NQPException("A dynamic array can only be indexed with a single dimension");
901919
}
902920
return array[idx[0]];
921+
} else {
922+
return array.$$atposnd(idx);
903923
}
904924
};
905925

906-
op.atposnd_n = op.atposnd;
907-
op.atposnd_s = op.atposnd;
908-
op.atposnd_i = op.atposnd;
909926

910-
op.bindposnd = function(ctx, array, idx, value) {
927+
928+
op.bindposnd = function(array, idx, value) {
911929
if (array instanceof Array) {
912930
if (idx.length != 1) {
913-
ctx.die("A dynamic array can only be indexed with a single dimension");
931+
throw new NQPException("A dynamic array can only be indexed with a single dimension");
914932
}
915933
return (array[idx[0]] = value);
934+
} else {
935+
return array.$$bindposnd(idx, value);
916936
}
917937
};
918938

919-
op.bindposnd_n = op.bindposnd;
920-
op.bindposnd_s = op.bindposnd;
921-
op.bindposnd_i = op.bindposnd;
939+
// TODO optimize
940+
op.atpos2d = function(array, x, y) {
941+
return op.atposnd(array, [x, y]);
942+
};
943+
944+
op.atpos3d = function(array, x, y, z) {
945+
return op.atposnd(array, [x, y, z]);
946+
};
947+
948+
op.bindpos2d = function(array, x, y, value) {
949+
return op.bindposnd(array, [x, y], value);
950+
};
951+
952+
op.bindpos3d = function(array, x, y, z, value) {
953+
return op.bindposnd(array, [x, y, z], value);
954+
};
955+
956+
['bindpos', 'atpos'].forEach(function(prefix) {
957+
['_n', '_s', '_i'].forEach(function(type) {
958+
['2d', '3d', 'nd'].forEach(function(dim) {
959+
op[prefix + dim + type] = op[prefix + dim];
960+
});
961+
});
962+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function NQPException(message) {
2+
this.name = 'NQPException';
3+
this.message = message;
4+
}
5+
6+
NQPException.prototype = Object.create(Error.prototype);
7+
NQPException.prototype.constructor = NQPException;
8+
9+
module.exports = NQPException;

src/vm/js/nqp-runtime/reprs.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var sixmodel = require('./sixmodel.js');
22
var Hash = require('./hash.js');
33
var NQPInt = require('./nqp-int.js');
4+
var NQPException = require('./nqp-exception.js');
45

56
function basic_type_object_for(HOW) {
67
var st = new sixmodel.STable(this, HOW);
@@ -738,3 +739,112 @@ function ConditionVariable() {}
738739
ConditionVariable.prototype.create_obj_constructor = basic_constructor;
739740

740741
module.exports.ConditionVariable = ConditionVariable;
742+
743+
function MultiDimArray() {
744+
745+
}
746+
MultiDimArray.prototype.type_object_for = basic_type_object_for;
747+
MultiDimArray.prototype.compose = function(STable, repr_info_hash) {
748+
var array = repr_info_hash.content.get('array');
749+
var dimensions = array.content.get('dimensions');
750+
751+
if (dimensions instanceof NQPInt) {
752+
dimensions = dimensions.value;
753+
if (dimensions === 0) {
754+
throw new NQPException("MultiDimArray REPR must be composed with at least 1 dimension");
755+
}
756+
757+
} else {
758+
throw "dimensions to MultiDimArray.compose must be a native int";
759+
}
760+
761+
// console.log('dimensions', dimensions);
762+
STable.dimensions = dimensions;
763+
};
764+
MultiDimArray.prototype.allocate = basic_allocate;
765+
766+
MultiDimArray.prototype.basic_constructor = basic_constructor;
767+
MultiDimArray.prototype.create_obj_constructor = function(STable) {
768+
var c = this.basic_constructor(STable);
769+
770+
STable.obj_constructor = c; // HACK it's set again later, we set it for addInternalMethod
771+
772+
STable.addInternalMethod('$$numdimensions', function(value) {
773+
if (this.type_object_) {
774+
throw new NQPException("Cannot get number of dimensions of a type object");
775+
}
776+
return STable.dimensions;
777+
});
778+
779+
STable.addInternalMethod('$$clone', function() {
780+
var clone = new this._STable.obj_constructor();
781+
clone.storage = this.storage.slice();
782+
clone.dimensions = this.dimensions;
783+
return clone;
784+
});
785+
786+
STable.addInternalMethod('$$dimensions', function() {
787+
if (this.type_object_) {
788+
throw new NQPException("Cannot get dimensions of a type object");
789+
}
790+
return this.dimensions;
791+
});
792+
793+
STable.addInternalMethod('$$setdimensions', function(value) {
794+
if (value.length != STable.dimensions) {
795+
throw new NQPException("Array type of " + STable.dimensions + " dimensions cannot be intialized with " + value.length + " dimensions");
796+
} else if (this.dimensions) {
797+
throw new NQPException("Can only set dimensions once");
798+
}
799+
this.storage = [];
800+
return (this.dimensions = value);
801+
});
802+
803+
804+
STable.addInternalMethod('$$calculateIndex', function(idx, value) {
805+
if (idx.length != STable.dimensions) {
806+
throw new NQPException("Cannot access " + STable.dimensions + " dimension array with " + idx.length + " indices");
807+
}
808+
809+
for (var i=0; i < idx.length; i++) {
810+
if (idx[i] < 0 || idx[i] >= this.dimensions[i]) {
811+
throw new NQPException("Index " + idx[i] + " for dimension " + (i+1) + " out of range (must be 0.." + this.dimensions[i] + ")");
812+
}
813+
}
814+
var calculatedIdx = 0;
815+
for (var i=0; i < idx.length; i++) {
816+
calculatedIdx = calculatedIdx * this.dimensions[i] + idx[i];
817+
}
818+
return calculatedIdx;
819+
});
820+
821+
STable.addInternalMethod('$$atposnd', function(idx) {
822+
return this.storage[this.$$calculateIndex(idx)];
823+
});
824+
825+
STable.addInternalMethod('$$bindposnd', function(idx, value) {
826+
return (this.storage[this.$$calculateIndex(idx)] = value);
827+
});
828+
829+
// TODO optimize access
830+
STable.addInternalMethod('$$bindpos', function(index, value) {
831+
return this.$$bindposnd([index], value);
832+
});
833+
834+
STable.addInternalMethod('$$setelems', function(elems) {
835+
this.$$setdimensions([elems]);
836+
});
837+
838+
STable.addInternalMethod('$$elems', function(elems) {
839+
return this.dimensions[0];
840+
});
841+
842+
STable.addInternalMethod('$$atpos', function(index) {
843+
return this.$$atposnd([index]);
844+
});
845+
846+
847+
return c;
848+
};
849+
850+
module.exports.MultiDimArray = MultiDimArray;

src/vm/js/nqp-runtime/runtime.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var op = {};
22
exports.op = op;
33

44
var NQPInt = require("./nqp-int.js");
5+
var NQPException = require("./nqp-exception.js");
56
exports.NQPInt = NQPInt;
67

78
function load_ops(module) {
@@ -221,6 +222,11 @@ Ctx.prototype.propagateException = function(exception) {
221222
throw exception.msg;
222223
};
223224

225+
Ctx.prototype.catchException = function(exception) {
226+
this.exception = exception;
227+
this.CATCH();
228+
};
229+
224230
Ctx.prototype.rethrow = function(exception) {
225231
exception.caught.caller.propagateException(exception);
226232
};
@@ -439,3 +445,4 @@ function runCPS(thunk_) {
439445
}
440446

441447
exports.runCPS = runCPS;
448+
exports.NQPException = NQPException;

src/vm/js/nqp-runtime/sixmodel.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ function STable(REPR, HOW) {
66

77

88
/* HACK - it's a bit hackish - think how correct it is */
9-
this.obj_constructor.prototype.$$clone = function() {
10-
var clone = new this._STable.obj_constructor();
11-
for (var i in this) {
12-
if (this.hasOwnProperty(i) && i != '_SC') {
13-
clone[i] = this[i];
9+
if (!this.obj_constructor.prototype.hasOwnProperty('$$clone')) {
10+
this.obj_constructor.prototype.$$clone = function() {
11+
var clone = new this._STable.obj_constructor();
12+
for (var i in this) {
13+
if (this.hasOwnProperty(i) && i != '_SC') {
14+
clone[i] = this[i];
15+
}
1416
}
15-
}
16-
return clone;
17-
};
17+
return clone;
18+
};
19+
}
1820

1921
/* Default boolification mode 5 */
2022
this.obj_constructor.prototype.$$to_bool = function(ctx) {

0 commit comments

Comments
 (0)