Skip to content

Commit 28e97f9

Browse files
committed
[js] Improve JS interop
1 parent e83fb41 commit 28e97f9

File tree

3 files changed

+116
-10
lines changed

3 files changed

+116
-10
lines changed

src/vm/js/Operations.nqp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,8 @@ class QAST::OperationsJS {
19001900
add_simple_op('iseq_snfg', $T_INT, [$T_STR, $T_STR]);
19011901
add_simple_op('isne_snfg', $T_INT, [$T_STR, $T_STR]);
19021902

1903+
add_simple_op('getjsattr', $T_OBJ, [$T_OBJ, $T_STR], :decont(0), :ctx);
1904+
add_simple_op('setjsattr', $T_OBJ, [$T_OBJ, $T_STR, $T_OBJ], :decont(0, 2), :ctx, :side_effects);
19031905

19041906
method add_hll_unbox($hll, $type, $method_name) {
19051907
unless nqp::existskey(%hll_unbox, $hll) {

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

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ const NativeUIntArg = nativeArgs.NativeUIntArg;
194194
const NativeNumArg = nativeArgs.NativeNumArg;
195195
const NativeStrArg = nativeArgs.NativeStrArg;
196196

197+
const NativeNumRet = nativeArgs.NativeNumRet;
198+
197199
const intToObj = exports.intToObj = function(currentHLL, i) {
198200
const type = currentHLL.get('int_box');
199201
if (!type) {
@@ -615,28 +617,87 @@ class WrappedFunction extends NQPObject {
615617
for (let i = 2; i < args.length; i++) {
616618
converted.push(/*await*/ toJS(args[i]));
617619
}
618-
return fromJS(this.func.apply(null, converted));
620+
return fromJSToReturnValue(args[0], this.func.apply(null, converted));
619621
}
620622

621623
$$call(args) {
622624
return this.$$apply(arguments);
623625
}
624626
};
625627

626-
function fromJS(obj) {
628+
function fromJSToReturnValue(ctx, obj) {
629+
return fromJS(ctx.$$getHLL(), obj, true, false);
630+
}
631+
632+
exports.fromJSToReturnValue = fromJSToReturnValue;
633+
634+
function fromJSToArgument(obj) {
635+
return fromJS(hll.getHLL('perl6'), obj, false, true);
636+
}
637+
638+
function fromJSToObject(ctx, obj) {
639+
return fromJS(hll.getHLL('perl6'), obj, false, false);
640+
}
641+
642+
function fromJS(HLL, obj, isReturnValue, isArgument) {
627643
if (typeof obj === 'function') {
628644
return new WrappedFunction(obj);
629645
} else if (obj === undefined || obj === null) {
630-
return Null;
646+
return HLL.get('null_value');
647+
} else if (obj === true) {
648+
return HLL.get('true_value');
649+
} else if (obj === false) {
650+
return HLL.get('false_value');
631651
} else if (typeof obj === 'number') {
632-
return new NQPNum(obj);
652+
if (isReturnValue) {
653+
return new NativeNumRet(obj);
654+
} else if (isArgument) {
655+
return new NativeNumArg(obj);
656+
} else {
657+
const type = HLL.get('int_box');
658+
const boxed = type._STable.REPR.allocate(type._STable);
659+
boxed.$$setInt(obj);
660+
return boxed;
661+
}
633662
} else if (typeof obj === 'string') {
634-
return new NQPStr(obj);
635-
} else {
663+
if (isReturnValue) {
664+
return obj;
665+
} else if (isArgument) {
666+
return new NativeStrArg(obj);
667+
} else {
668+
const type = HLL.get('str_box');
669+
const boxed = type._STable.REPR.allocate(type._STable);
670+
boxed.$$setStr(obj);
671+
return boxed;
672+
}
673+
} else if (obj.$$decont) {
674+
return obj;
675+
} else if (obj instanceof EvalResult) {
636676
return obj;
677+
} else {
678+
const type = HLL.get('js_box');
679+
const wrapped = type._STable.REPR.allocate(type._STable);
680+
wrapped.$$jsObject = obj;
681+
return wrapped;
682+
}
683+
}
684+
685+
function toJSWithCtx(ctx, obj) {
686+
const HLL = ctx.$$getHLL();
687+
if (obj.$$istype(ctx, HLL.get('str_box'))) {
688+
return obj.$$getStr();
689+
} else if (obj.$$istype(ctx, HLL.get('num_box'))) {
690+
return obj.$$getNum();
691+
} else if (obj.$$istype(ctx, HLL.get('js_box'))) {
692+
return obj.$$jsObject;
693+
} else {
694+
require('nqp-runtime').dumpObj(obj);
695+
console.trace(`Can't unbox`);
637696
}
638697
}
639698

699+
exports.toJSWithCtx = toJSWithCtx;
700+
640701
function toJS(obj) {
641702
if (obj instanceof NativeIntArg || obj instanceof NativeUIntArg) {
642703
return obj.value;
@@ -650,7 +711,7 @@ function toJS(obj) {
650711
return function() {
651712
const converted = [null, {}];
652713
for (let i = 0; i < arguments.length; i++) {
653-
converted.push(fromJS(arguments[i]));
714+
converted.push(fromJSToArgument(arguments[i]));
654715
}
655716
return toJS(obj.$$apply(converted));
656717
};
@@ -796,7 +857,8 @@ class JavaScriptCompiler extends NQPObject {
796857

797858
eval(ctx, _NAMED, self, code) {
798859
if (!(_NAMED !== null && _NAMED.hasOwnProperty('mapping'))) {
799-
return fromJS(eval(nqp.arg_s(ctx, code)));
860+
let codeStr = nqp.arg_s(ctx, code);
861+
return fromJSToReturnValue(ctx, eval(codeStr));
800862
}
801863

802864
const fakeFilename = 'nqpEval' + shortid.generate();
@@ -840,7 +902,7 @@ class JavaScriptCompiler extends NQPObject {
840902
return require(path);
841903
};
842904

843-
const ret = fromJS(compiled());
905+
const ret = fromJSToReturnValue(ctx, compiled());
844906
global.nqpRequire = oldNqpRequire;
845907

846908
return ret;
@@ -854,7 +916,7 @@ class JavaScriptCompiler extends NQPObject {
854916

855917
const codeRef = new CodeRef();
856918
codeRef.$$call = function(ctx, _NAMED) {
857-
return fromJS(compiled());
919+
return fromJSToReturnValue(ctx, compiled());
858920
};
859921
return codeRef;
860922
}
@@ -1994,3 +2056,11 @@ op.iseq_snfg = function(a, b) {
19942056
op.isne_snfg = function(a, b) {
19952057
return (a.normalize('NFC') === b.normalize('NFC')) ? 0 : 1;
19962058
};
2059+
2060+
op.setjsattr = function(ctx, obj, attr, value) {
2061+
return obj.$$jsObject[attr] = toJSWithCtx(ctx, value);
2062+
};
2063+
2064+
op.getjsattr = function(ctx, obj, attr) {
2065+
return fromJSToObject(ctx, obj.$$jsObject[attr]);
2066+
};

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,6 +2469,40 @@ reprs.CUnion = CUnion;
24692469
reprs.CStruct = CUnion;
24702470

24712471

2472+
class WrappedJSObject extends REPR {
2473+
createObjConstructor(STable) {
2474+
const ObjConstructor = function() {};
2475+
const handler = {};
2476+
handler.get = function(target, name) {
2477+
if (name.substr(0, 2) === '$$') {
2478+
return undefined;
2479+
}
2480+
2481+
return /*async*/ function() {
2482+
const converted = [];
2483+
for (let i = 3; i < arguments.length; i++) {
2484+
converted.push(/*await*/ core.toJSWithCtx(arguments[0], arguments[i].$$decont(arguments[0])));
2485+
}
2486+
2487+
if (this.$$jsObject[name]) {
2488+
return core.fromJSToReturnValue(arguments[0], this.$$jsObject[name].apply(this.$$jsObject, converted));
2489+
} else {
2490+
methodNotFoundError(arguments[0], this, name);
2491+
}
2492+
};
2493+
};
2494+
2495+
ObjConstructor.prototype = Object.create(new Proxy({}, handler));
2496+
ObjConstructor.prototype._STable = STable;
2497+
2498+
ObjConstructor.prototype._SC = undefined;
2499+
ObjConstructor.prototype._WHERE = undefined;
2500+
2501+
return ObjConstructor;
2502+
}
2503+
}
2504+
reprs.WrappedJSObject = WrappedJSObject;
2505+
24722506
let ID = 0;
24732507
for (const name in reprs) {
24742508
module.exports[name] = reprs[name];

0 commit comments

Comments
 (0)