src/values/LinkValue.js
'use strict';
/* @flow */
const Value = require('../Value');
const CompletionRecord = require('../CompletionRecord');
const ArrayValue = require('./ArrayValue');
function invoke(target, thiz, args) {
return Function.prototype.apply.call(target, thiz, args);
}
function invokeAsNew(target, args) {
if ( target.bind ) {
let bindArgs = [null].concat(args);
return new (target.bind.apply(target, bindArgs))();
} else {
return invoke(target, null, args);
}
}
/**
* Represents a value that maps directly to an untrusted local value.
*/
class LinkValue extends Value {
constructor(value, realm) {
super();
this.native = value;
this.realm = realm;
}
static make(native, realm) {
let wellKnown = realm.lookupWellKnown(native);
if ( wellKnown ) return wellKnown;
if ( Array.isArray(native) ) {
var ia = new Array(native.length);
for ( let i = 0; i < native.length; ++i ) {
ia[i] = LinkValue.make(native[i], realm);
}
return ArrayValue.make(ia, realm);
}
return new LinkValue(native, realm);
}
ref(name, s) {
let that = this;
let out = Object.create(null);
out.getValue = function *() { return yield * that.get(name, s); };
out.setValue = function *(to, s) { return yield * that.set(name, to, s); };
out.del = function() { return false; };
return out;
}
*set(name, value, s, extra) {
this.native[name] = value.toNative();
}
toNative() {
return this.native;
}
*asString() {
return this.native.toString();
}
makeLink(value) {
return this.realm.import(value, this.linkKind);
}
*doubleEquals(other) { return this.makeLink(this.native == other.toNative()); }
*tripleEquals(other) { return this.makeLink(this.native === other.toNative()); }
*add(other) { return this.makeLink(this.native + other.toNative()); }
*subtract(other) { return this.makeLink(this.native - other.toNative()); }
*multiply(other) { return this.makeLink(this.native * other.toNative()); }
*divide(other) { return this.makeLink(this.native / other.toNative()); }
*mod(other) { return this.makeLink(this.native % other.toNative()); }
*shiftLeft(other) { return this.makeLink(this.native << other.toNative()); }
*shiftRight(other) { return this.makeLink(this.native >> other.toNative()); }
*shiftRightZF(other) { return this.makeLink(this.native >>> other.toNative()); }
*bitAnd(other) { return this.makeLink(this.native & other.toNative()); }
*bitOr(other) { return this.makeLink(this.native | other.toNative()); }
*bitXor(other) { return this.makeLink(this.native ^ other.toNative()); }
*gt(other) { return this.makeLink(this.native > other.toNative()); }
*lt(other) { return this.makeLink(this.native < other.toNative()); }
*gte(other) { return this.makeLink(this.native >= other.toNative()); }
*lte(other) { return this.makeLink(this.native <= other.toNative()); }
*inOperator(other) { return this.makeLink(other.toNative() in this.native); }
*instanceOf(other) { return this.makeLink(this.native instanceof other.toNative()); }
*unaryPlus() { return this.makeLink(+this.native); }
*unaryMinus() { return this.makeLink(-this.native); }
*not() { return this.makeLink(!this.native); }
*get(name, realm, origional) {
let desc = Object.getOwnPropertyDescriptor(this.native, name);
if ( desc ) {
if ( desc.get && origional ) return this.makeLink(origional.native[name], realm);
return this.makeLink(this.native[name], realm);
}
return yield * this.makeLink(Object.getPrototypeOf(this.native), realm).get(name, realm, origional || this);
}
*observableProperties(realm) {
for ( let p in this.native ) {
yield this.makeLink(p);
}
return;
}
/**
*
* @param {Value} thiz
* @param {Value[]} args
* @param {Scope} s
*/
*call(thiz, args, s, ext) {
let realArgs = new Array(args.length);
for ( let i = 0; i < args.length; ++i ) {
realArgs[i] = args[i].toNative();
}
try {
let asConstructor = ext && ext.asConstructor;
let result;
if ( asConstructor ) result = invokeAsNew(this.native, realArgs);
else result = invoke(this.native, thiz ? thiz.toNative() : undefined, realArgs);
let val = this.makeLink(result, s.realm);
if ( typeof s.realm.options.linkValueCallReturnValueWrapper === 'function' ) {
val = s.realm.options.linkValueCallReturnValueWrapper(val);
}
return val;
} catch ( e ) {
let result = this.makeLink(e, s.realm);
return new CompletionRecord(CompletionRecord.THROW, result);
}
}
get isCallable() {
return ( typeof this.native === 'function' );
}
getPropertyValueMap() {
let list = {};
for ( let p in this.native ) {
let v = this.native[p];
list[p] = this.makeLink(v);
}
return list;
}
*toNumberValue() { return Value.fromNative((Number(this.native))); }
*toStringValue() { return Value.fromNative((String(this.native))); }
getPrototypeProperty() {
return this.makeLink(this.native.prototype);
}
getPrototype(realm) {
return realm.ObjectPrototype;
}
*makeThisForNew() {
return Value.undef;
}
get debugString() {
return '[Link: ' + this.native + ']';
}
get truthy() {
return !!this.native;
}
get jsTypeName() {
return typeof this.native;
}
*toPrimitiveValue(preferedType) {
switch ( preferedType ) {
case 'string':
return Value.fromNative(this.native.toString());
default:
return Value.fromNative(this.native.valueOf());
}
}
get linkKind() { return 'link'; }
}
module.exports = LinkValue;