Skip to content

Commit

Permalink
[js] Keep privates attributes from ancestor classes separate.
Browse files Browse the repository at this point in the history
  • Loading branch information
pmurias committed May 4, 2016
1 parent f280dff commit 25600fd
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 78 deletions.
42 changes: 33 additions & 9 deletions src/vm/js/Compiler.nqp
Expand Up @@ -1717,17 +1717,41 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
}
}
elsif ($var.scope eq 'attribute') {
# TODO take second argument into account
# TODO figure out if the second argument can be always assumed to be a WVal
# TODO types
my $self := self.as_js_clear_bindval($var[0], :want($T_OBJ), :$cps);
my $attr := Chunk.new($T_OBJ, "{$self.expr}[{quote_string($var.name)}]", [$self]);
if $*BINDVAL {
my $bindval := self.as_js_clear_bindval($*BINDVAL, :want($T_OBJ), :$cps);
Chunk.new($T_OBJ, $bindval.expr, [$attr, $bindval, "{$attr.expr} = {$bindval.expr};\n"]);

# Get lookup hint if possible.
my int $hint := -1;
if $var[1].has_compile_time_value {
$hint := nqp::hintfor($var[1].compile_time_value, $var.name);
}
else {
$attr;

my $self := self.as_js_clear_bindval($var[0], :want($T_OBJ), :$cps);

if $hint == -1 {
my $type := self.as_js_clear_bindval($var[1], :want($T_OBJ), :$cps);
my $name := quote_string($var.name);
if $*BINDVAL {
my $bindval := self.as_js_clear_bindval($*BINDVAL, :want($T_OBJ), :$cps);
Chunk.new($T_OBJ, $bindval.expr, [$self, $type, $bindval,
"{$self.expr}.\$\$bindattr({$type.expr}, $name, {$bindval.expr});\n"
]);
}
else {
$self.expr;
$type.expr;
Chunk.new($T_OBJ, "{$self.expr}.\$\$getattr({$type.expr}, $name)", [$self, $type]);
}
} else {
my $attr := Chunk.new($T_OBJ, "{$self.expr}.{'attr$' ~ $hint}", [$self]);
if $*BINDVAL {
my $bindval := self.as_js_clear_bindval($*BINDVAL, :want($T_OBJ), :$cps);
$attr.expr;
$bindval.expr;
Chunk.new($T_OBJ, $bindval.expr, [$attr, $bindval, "{$attr.expr} = {$bindval.expr};\n"]);
}
else {
$attr;
}
}
}
elsif ($var.scope eq 'contextual') {
Expand Down
33 changes: 17 additions & 16 deletions src/vm/js/Operations.nqp
Expand Up @@ -161,33 +161,34 @@ class QAST::OperationsJS {

add_simple_op('clone', $T_OBJ, [$T_OBJ]);

# TODO handle attributes properly
# TODO optimize cases where the class and the attribute are constants
for ['', $T_OBJ, '_i', $T_INT, '_n', $T_NUM, '_s', $T_STR] -> $suffix, $type {
add_simple_op('bindattr' ~ $suffix, $type, [$T_OBJ, $T_OBJ, $T_STR, $type], :sideffects,
sub ($obj, $type, $attr, $value) {
# TODO take second argument into account
"($obj[$attr] = $value)";
}
);

add_op('bindattr' ~ $suffix, sub ($comp, $node, :$want, :$cps) {
my $obj := $comp.as_js(:want($T_OBJ), $node[0]);
my $classHandle := $comp.as_js(:want($T_OBJ), $node[1]);
my $attrName := $comp.as_js(:want($T_STR), $node[2]);
my $value := $comp.as_js(:want($type), $node[3]);

$comp.stored_result(Chunk.new($type,
"{$obj.expr}\.\$\$bindattr({$classHandle.expr}, {$attrName.expr}, {$value.expr})",
[$obj, $classHandle, $attrName, $value]));
});

add_simple_op('getattr' ~ $suffix, $type, [$T_OBJ, $T_OBJ, $T_STR],
sub ($obj, $type, $attr) {
# TODO take second argument into account
"$obj[$attr]";
"$obj\.\$\$getattr($type, $attr)";
}
);
}

# HACK - we need this until we handle types on attributes properly
add_simple_op('getattr_i', $T_INT, [$T_OBJ, $T_OBJ, $T_STR], sub ($obj, $type, $attr) {
"nqp.intAttrHack($obj[$attr])"
"nqp.intAttrHack($obj\.\$\$getattr($type, $attr))"
});

add_simple_op('attrinited', $T_BOOL, [$T_OBJ, $T_OBJ, $T_STR],
sub ($obj, $type, $attr) {
# TODO take second argument into account
"$obj.hasOwnProperty($attr)";
}
);

add_simple_op('hintfor', $T_INT, [$T_OBJ, $T_STR]);


add_hll_op('sprintf');
Expand Down
8 changes: 4 additions & 4 deletions src/vm/js/RegexCompiler.nqp
Expand Up @@ -128,12 +128,12 @@ class RegexCompiler {
my $scan := self.new_label;
my $done := self.new_label;

"if ({$!compiler.mangle_name('self')}['\$!from'] != -1) \{{self.goto($done)}\}\n" # HACK
"if (nqp.getattrHack({$!compiler.mangle_name('self')},'\$!from') != -1) \{{self.goto($done)}\}\n"
~ self.goto($scan)
~ self.case($loop)
~ "$!pos++;\n"
~ "if ($!pos >= $!target.length) \{{self.fail}\}\n"
~ "$!cursor['\$!from'] = $!pos;\n" # HACK
~ "nqp.bindattrHack($!cursor, '\$!from', $!pos);\n"
~ self.case($scan)
~ self.mark($loop,$!pos,0)
~ self.case($done);
Expand Down Expand Up @@ -273,12 +273,12 @@ class RegexCompiler {

# TODO proper $!pos access
method pos_from_cursor($cursor) {
"nqp.toInt($cursor['\$!pos'], $*CTX)";
"nqp.toInt(nqp.getattrHack($cursor, '\$!pos'), $*CTX)";
}

# TODO proper $!pos access
method set_cursor_pos() {
"$!cursor['\$!pos\'] = $!pos;\n";
"nqp.bindattrHack($!cursor, '\$!pos\', $!pos);\n";
}

method subrule($node) {
Expand Down
5 changes: 4 additions & 1 deletion src/vm/js/nqp-runtime/deserialization.js
Expand Up @@ -345,7 +345,6 @@ BinaryCursor.prototype.STable = function(STable) {
this.str(); // md_valid_attr_name
this.varint(); // md_valid_hint

STable.setinvokespec(classHandle, attrName, invocationHandler);
}

if (flags & STABLE_HAS_HLL_OWNER) {
Expand All @@ -361,6 +360,10 @@ BinaryCursor.prototype.STable = function(STable) {
STable.REPR.deserializeReprData(this.clone(), STable);
}

if (flags & STABLE_HAS_INVOCATION_SPEC) {
STable.setinvokespec(classHandle, attrName, invocationHandler);
}


};

Expand Down
111 changes: 65 additions & 46 deletions src/vm/js/nqp-runtime/reprs.js
Expand Up @@ -31,6 +31,10 @@ function basicConstructor(STable) {
return objConstructor;
}

function slotToAttr(slot) {
return 'attr$' + slot;
}

function P6opaque() {
}

Expand All @@ -49,7 +53,6 @@ P6opaque.prototype.precalculate = function() {
if (this.autoVivValues) {
for (var i in this.nameToIndexMapping) {
for (var j in this.nameToIndexMapping[i].slots) {
var name = this.nameToIndexMapping[i].names[j];
var slot = this.nameToIndexMapping[i].slots[j];
if (this.autoVivValues[slot]) {
if (!this.autoVivValues[slot].typeObject_) {
Expand All @@ -58,10 +61,10 @@ P6opaque.prototype.precalculate = function() {
}
/* TODO autoviving things that aren't typeobjects */
/* TODO we need to store attributes better */
autovived[name] = this.autoVivValues[slot];
autovived[slotToAttr(slot)] = this.autoVivValues[slot];
} else if (this.flattenedStables[slot]) {
if (this.flattenedStables[slot].REPR.flattenedDefault !== undefined) {
autovived[name] = this.flattenedStables[slot].REPR.flattenedDefault;
autovived[slotToAttr(slot)] = this.flattenedStables[slot].REPR.flattenedDefault;
}
}
}
Expand All @@ -73,11 +76,11 @@ P6opaque.prototype.precalculate = function() {
}

this.template = {};
/* TODO take classes into account when storing attributes */
/* TODO think about attribute types */
for (var i in this.nameToIndexMapping) {
for (var j in this.nameToIndexMapping[i].slots) {
var name = this.nameToIndexMapping[i].names[j];
this.template[name] = null;
var slot = this.nameToIndexMapping[i].slots[j];
this.template[slotToAttr(slot)] = null;
}
}

Expand Down Expand Up @@ -153,22 +156,53 @@ P6opaque.prototype.deserializeReprData = function(cursor, STable) {
this.associativeDelegateSlot = cursor.varint();

if (this.positionalDelegateSlot != -1) {
STable.setPositionalDelegate(slots[this.positionalDelegateSlot]);
STable.setPositionalDelegate(slotToAttr(this.positionalDelegateSlot));
}
if (this.associativeDelegateSlot != -1) {
STable.setAssociativeDelegate(slots[this.associativeDelegateSlot]);
STable.setAssociativeDelegate(slotToAttr(this.associativeDelegateSlot));
}

if (this.unboxSlots) {
for (var i = 0; i < this.unboxSlots.length; i++) {
var slot = this.unboxSlots[i].slot;
(new reprById[this.unboxSlots[i].reprId]).generateBoxingMethods(STable, slots[slot], this.flattenedStables[slot]);
(new reprById[this.unboxSlots[i].reprId]).generateBoxingMethods(STable, slotToAttr(slot), this.flattenedStables[slot]);
}
}

/* TODO make auto viv values work */
};


P6opaque.prototype.hintfor = function(classHandle, attrName) {
for (var i=0;i < this.nameToIndexMapping.length; i++) {
if (this.nameToIndexMapping[i].classKey === classHandle) {
for (var j=0;j < this.nameToIndexMapping[i].slots.length; j++) {
if (this.nameToIndexMapping[i].names[j] === attrName) {
return this.nameToIndexMapping[i].slots[j];
}
}
}
}
return -1;
};

P6opaque.prototype.getAttr = function(classHandle, attrName) {
var nqp = require('nqp-runtime');
var hint = this.hintfor(classHandle, attrName);
if (hint == -1) {
console.log('classHandle', classHandle);
if (classHandle === null) {
console.trace("null classHandle");
process.exit();
}
console.trace("getAttr", attrName);
console.log(nqp.dumpObj(this.nameToIndexMapping));
throw "Can't find: " + attrName;
} else {
return slotToAttr(hint);
}
};

P6opaque.prototype.serializeReprData = function(st, cursor) {
var numAttrs = st.REPR.flattenedStables.length;
cursor.varint(numAttrs);
Expand Down Expand Up @@ -239,17 +273,6 @@ P6opaque.prototype.serializeReprData = function(st, cursor) {
P6opaque.prototype.deserializeFinish = function(obj, data) {
var attrs = [];

var names = {};

for (var i in this.nameToIndexMapping) {
for (var j in this.nameToIndexMapping[i].slots) {
var name = this.nameToIndexMapping[i].names[j];
var slot = this.nameToIndexMapping[i].slots[j];
// TODO take class key into account with attribute storage
names[slot] = name;
}
}

for (var i = 0; i < this.flattenedStables.length; i++) {
if (this.flattenedStables[i]) {
var STable = this.flattenedStables[i];
Expand All @@ -264,10 +287,8 @@ P6opaque.prototype.deserializeFinish = function(obj, data) {

for (var i in this.nameToIndexMapping) {
for (var j in this.nameToIndexMapping[i].slots) {
var name = this.nameToIndexMapping[i].names[j];
var slot = this.nameToIndexMapping[i].slots[j];
// TODO take class key into account with attribute storage
obj[name] = attrs[slot];
obj[slotToAttr(slot)] = attrs[slot];
}
}
};
Expand All @@ -279,30 +300,16 @@ P6opaque.prototype.serialize = function(cursor, obj) {
throw 'Representation must be composed before it can be serialized';
}

var attrs = [];

var names = [];

for (var i in this.nameToIndexMapping) {
for (var j in this.nameToIndexMapping[i].slots) {
var name = this.nameToIndexMapping[i].names[j];
var slot = this.nameToIndexMapping[i].slots[j];

// TODO take class key into account with attribute storage
attrs[slot] = obj[name];
names[slot] = name;
}
}

for (var i = 0; i < flattened.length; i++) {
var value = obj[slotToAttr(i)];
if (flattened[i] == null || !flattened[i]) {
// TODO - think about what happens when we get an undefined value here
cursor.ref(attrs[i]);
cursor.ref(value);
}
else {
// HACK different kinds of numbers etc.
var attr = typeof attrs[i] == 'object' ? attrs[i] : {value: attrs[i]}; // HACK - think if that's a correct way of serializing a native attribute
this.flattenedStables[i].REPR.serialize(cursor, attr);
var wrapped = typeof value == 'object' ? value : {value: value}; // HACK - think if that's a correct way of serializing a native attribute
this.flattenedStables[i].REPR.serialize(cursor, wrapped);
}
}
};
Expand Down Expand Up @@ -362,13 +369,13 @@ P6opaque.prototype.compose = function(STable, reprInfoHash) {
for (var j = 0; j < numAttrs; j++) {
var attr = attrs[j].content;

var type = attr.get('type');
var attrType = attr.get('type');
/* old boxing method generation */
if (attr.get('box_target')) {
var REPR = type._STable.REPR;
var REPR = attrType._STable.REPR;
if (!this.unboxSlots) this.unboxSlots = [];
this.unboxSlots.push({slot: curAttr, reprId: REPR.ID});
REPR.generateBoxingMethods(STable, attr.get('name'), type._STable);
REPR.generateBoxingMethods(STable, slotToAttr(curAttr), attrType._STable);
}

/* TODO */
Expand All @@ -384,8 +391,8 @@ P6opaque.prototype.compose = function(STable, reprInfoHash) {

/* HACK we don't actually implement STable inlining, but just pass around the STable
to make boxing of bignums work */
if (attr.get('box_target') && type._STable.REPR.flattenSTable) {
this.flattenedStables.push(type._STable);
if (attr.get('box_target') && attrType._STable.REPR.flattenSTable) {
this.flattenedStables.push(attrType._STable);
} else {
this.flattenedStables.push(null);
}
Expand Down Expand Up @@ -458,6 +465,18 @@ P6opaque.prototype.compose = function(STable, reprInfoHash) {

};

P6opaque.prototype.setup_STable = function(STable) {
var repr = this;
STable.addInternalMethod('$$bindattr', function(classHandle, attrName, value) {
this[repr.getAttr(classHandle, attrName)] = value;
return value;
});

STable.addInternalMethod('$$getattr', function(classHandle, attrName, value) {
return this[repr.getAttr(classHandle, attrName)];
});
};

reprs.P6opaque = P6opaque;

function KnowHOWREPR() {
Expand Down
20 changes: 20 additions & 0 deletions src/vm/js/nqp-runtime/runtime.js
Expand Up @@ -419,5 +419,25 @@ function runCPS(thunk_) {
}
}


function hack(obj, attrName) {
var repr = obj._STable.REPR;
for (var i=0;i < repr.nameToIndexMapping.length; i++) {
for (var j=0;j < repr.nameToIndexMapping[i].slots.length; j++) {
if (repr.nameToIndexMapping[i].names[j] === attrName) {
return repr.nameToIndexMapping[i].slots[j];
}
}
}
}

exports.bindattrHack = function(obj, attr, value) {
return (obj['attr$' + hack(obj, attr)] = value);
};

exports.getattrHack = function(obj, attr) {
return (obj['attr$' + hack(obj, attr)]);
};

exports.runCPS = runCPS;
exports.NQPException = NQPException;

0 comments on commit 25600fd

Please sign in to comment.