Skip to content

Commit

Permalink
Emit IR for super stores
Browse files Browse the repository at this point in the history
Summary:
Implement `super` stores by using `StorePropertyWithReceiverInst`.

The model for how this is expressed in the spec can be found here
https://tc39.es/ecma262/#sec-makesuperpropertyreference

Reviewed By: avp

Differential Revision: D66469615

fbshipit-source-id: 785c92ab3568d1268954e0b96de683539ca13da3
  • Loading branch information
fbmal7 authored and facebook-github-bot committed Jan 16, 2025
1 parent 5294f31 commit 1b886b6
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 6 deletions.
16 changes: 15 additions & 1 deletion lib/IRGen/ESTreeIRGen-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,21 @@ void ESTreeIRGen::emitMemberStore(
ESTree::MemberExpressionNode *mem,
Value *storedValue,
Value *baseValue,
Value *propValue) {
Value *propValue,
Value *thisValue) {
// If \p thisValue is set, this is a store to `super`, so we must set up the
// receiver correctly.
if (thisValue) {
Builder.createStorePropertyWithReceiverInst(
storedValue,
baseValue,
propValue,
thisValue,
curFunction()->function->isStrictMode() ? IRBuilder::StoreStrict::Yes
: IRBuilder::StoreStrict::No);
return;
}

if (auto *classType = llvh::dyn_cast<flow::ClassType>(
flowContext_.getNodeTypeOrAny(mem->_object)->info)) {
if (!mem->_computed) {
Expand Down
29 changes: 26 additions & 3 deletions lib/IRGen/ESTreeIRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ void LReference::emitStore(Value *value) {
llvh::cast<ESTree::MemberExpressionNode>(ast_),
value,
base_,
property_);
property_,
thisValue_);
case Kind::VarOrGlobal:
irgen_->emitStore(value, base_, declInit_);
return;
Expand Down Expand Up @@ -454,10 +455,32 @@ LReference ESTreeIRGen::createLRef(ESTree::Node *node, bool declInit) {
/// Create lref for member expression (ex: o.f).
if (auto *ME = llvh::dyn_cast<ESTree::MemberExpressionNode>(node)) {
LLVM_DEBUG(llvh::dbgs() << "Creating an LRef for member expression.\n");
Value *obj = genExpression(ME->_object);

Value *obj;
if (llvh::isa<ESTree::SuperNode>(ME->_object)) {
auto *homeObjectVar = curFunction()->capturedState.homeObject;
auto *RSI = emitResolveScopeInstIfNeeded(homeObjectVar->getParent());
Value *homeObjectVal = Builder.createLoadFrameInst(RSI, homeObjectVar);
obj = Builder.createLoadParentNoTrapsInst(homeObjectVal);
} else {
obj = genExpression(ME->_object);
}

Value *prop = genMemberExpressionProperty(ME);
Value *thisVal = nullptr;
// `thisVal` should only be set for `super` references.
if (llvh::isa<ESTree::SuperNode>(ME->_object)) {
thisVal = genThisExpression();
}
return LReference(
LReference::Kind::Member, this, false, ME, obj, prop, sourceLoc);
LReference::Kind::Member,
this,
false,
ME,
obj,
prop,
sourceLoc,
thisVal);
}

/// Create lref for identifiers (ex: a).
Expand Down
13 changes: 11 additions & 2 deletions lib/IRGen/ESTreeIRGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,13 @@ class LReference {
ESTree::Node *ast,
Value *base,
Value *property,
SMLoc loadLoc)
SMLoc loadLoc,
Value *thisVal = nullptr)
: kind_(kind), irgen_(irgen), declInit_(declInit) {
ast_ = ast;
base_ = base;
property_ = property;
thisValue_ = thisVal;
loadLoc_ = loadLoc;
}

Expand Down Expand Up @@ -423,6 +425,10 @@ class LReference {
/// The name/value of the field this reference accesses, or null if this
/// is a variable access.
Value *property_;

/// Corresponds to [[ThisValue]] in ES15 6.2.5 Reference Record. Only
/// populated for super references.
Value *thisValue_;
};

/// Destructuring assignment target.
Expand Down Expand Up @@ -870,11 +876,14 @@ class ESTreeIRGen {
ESTree::MemberExpressionNode *mem,
Value *baseValue,
Value *propValue);
/// \param thisValue is the value of `this` when the member expression was
/// first evaluated. This is only populated in `super` references.
void emitMemberStore(
ESTree::MemberExpressionNode *mem,
Value *storedValue,
Value *baseValue,
Value *propValue);
Value *propValue,
Value *thisValue);

/// emitMemberStore for the specific case where the member is a typed class
/// field.
Expand Down
65 changes: 65 additions & 0 deletions test/hermes/super-references.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,68 @@ c3.asyncFun();
child.foo();
//CHECK-NEXT: 20
})();

// Test that the receiver is set up correctly for writes.
(function () {
var parent = {
x: 30,
set prop1(value) {
print(this.x);
}
}
var child = {
x: 40,
foo() {
super.prop1 = "value";
}
}
Object.setPrototypeOf(child, parent);
child.foo();
//CHECK-NEXT: 40
})();

// Test that super writes throw under correct conditions.

// Should not throw
(function () {
var parent = {}
Object.defineProperty(parent, 'prop1', {
value: 50,
writable: false
});
var child = {
foo() {
super.prop1 = "value";
}
};
Object.setPrototypeOf(child, parent);
// This doesn't throw.
child.foo();
print(child.prop1);
//CHECK-NEXT: 50
print(parent.prop1);
//CHECK-NEXT: 50
})();

// Should throw
(function () {
"use strict";
var parent = {}
Object.defineProperty(parent, 'prop1', {
value: 50,
writable: false
});
var child = {
foo() {
super.prop1 = "value";
}
};
Object.setPrototypeOf(child, parent);
try {
child.foo();
print("Fail");
} catch (e) {
print("Pass");
}
//CHECK-NEXT: Pass
})();

0 comments on commit 1b886b6

Please sign in to comment.