Skip to content

Commit

Permalink
add with statement support
Browse files Browse the repository at this point in the history
  • Loading branch information
trossimel-sc committed Nov 27, 2024
2 parents f6779f9 + f0d9e14 commit 82eaf24
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 48 deletions.
13 changes: 13 additions & 0 deletions include/hermes/Sema/SemContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,17 @@ class SemContext {
return root_->bindingTable_;
}

uint32_t getWithLexicalDepth(ESTree::WithStatementNode * node) const {
if(auto it = withDepths.find(node); it != withDepths.end())
return it->second;
assert(false && "with statement must have been processed before attempting to get the depth");
return 0;
}

void setWithLexicalDepth(ESTree::WithStatementNode * node, uint32_t depth) {
withDepths[node] = depth;
}

private:
/// The parent SemContext of this SemContext.
/// If null, this SemContext has no parent.
Expand Down Expand Up @@ -517,6 +528,8 @@ class SemContext {
/// "expression decl" are both set and are not the same value.
llvh::DenseMap<ESTree::IdentifierNode *, Decl *>
sideIdentifierDeclarationDecl_{};

llvh::DenseMap<ESTree::WithStatementNode *, uint32_t> withDepths{};
};

class SemContextDumper {
Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ add_hermes_library(hermesFrontend
IRGen/ESTreeIRGen-func.cpp
IRGen/ESTreeIRGen-except.cpp
IRGen/ESTreeIRGen-typed-class.cpp
IRGen/ESTreeIRGen-with.cpp
IR/IR.cpp
IR/CFG.cpp
IR/IRBuilder.cpp
Expand Down
77 changes: 65 additions & 12 deletions lib/IRGen/ESTreeIRGen-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,10 @@ Value *ESTreeIRGen::genCallExpr(ESTree::CallExpressionNode *call) {
// Must call the field init function immediately after.
fieldInitClassType = curFunction()->typedClassContext.type;
} else {
thisVal = Builder.getLiteralUndefined();
thisVal = withAwareEmitLoad(
nullptr,
call->_callee,
ConditionalChainType::OBJECT_ONLY_WITH_UNDEFINED_ALTERNATE);
callee = genExpression(call->_callee);
}

Expand Down Expand Up @@ -588,7 +591,10 @@ Value *ESTreeIRGen::genOptionalCallExpr(
thisVal = Builder.getLiteralUndefined();
callee = genOptionalCallExpr(oce, shortCircuitBB);
} else {
thisVal = Builder.getLiteralUndefined();
thisVal = withAwareEmitLoad(
nullptr,
call->_callee,
ConditionalChainType::OBJECT_ONLY_WITH_UNDEFINED_ALTERNATE);
callee = genExpression(getCallee(call));
}

Expand Down Expand Up @@ -2068,15 +2074,32 @@ Value *ESTreeIRGen::genUnaryExpression(ESTree::UnaryExpressionNode *U) {
Identifier name = getNameFieldFromID(iden);
auto *var = resolveIdentifier(iden);

if (llvh::isa<GlobalObjectProperty>(var)) {
// If the variable doesn't exist or if it is global, we must generate
// a delete global property instruction.
return Builder.createDeletePropertyInst(
Builder.getGlobalObject(), Builder.getLiteralString(name));
if (withScopes_.empty()) {
if (llvh::isa<GlobalObjectProperty>(var)) {
// If the variable doesn't exist or if it is global, we must generate
// a delete global property instruction.
return Builder.createDeletePropertyInst(
Builder.getGlobalObject(), Builder.getLiteralString(name));
} else {
// Otherwise it is a local variable which can't be deleted and we just
// return false.
return Builder.getLiteralBool(false);
}
} else {
// Otherwise it is a local variable which can't be deleted and we just
// return false.
return Builder.getLiteralBool(false);
auto withObj = withAwareEmitLoad(
var,
iden,
llvh::isa<GlobalObjectProperty>(var)
? ConditionalChainType::OBJECT_ONLY_WITH_GLOBAL_ALTERNATE
: ConditionalChainType::OBJECT_ONLY_WITH_UNDEFINED_ALTERNATE);

return genConditionalExpr(
[&]() -> Value * { return withObj; },
[&]() -> Value * {
return Builder.createDeletePropertyInst(
withObj, Builder.getLiteralString(name));
},
[&]() -> Value * { return Builder.getLiteralBool(false); });
}
}

Expand Down Expand Up @@ -2337,6 +2360,36 @@ Value *ESTreeIRGen::genLogicalAssignmentExpr(
return Builder.createPhiInst(std::move(values), std::move(blocks));
}

Value *ESTreeIRGen::genConditionalExpr(
const std::function<Value *()> &conditionGenerator,
const std::function<Value *()> &consequentGenerator,
const std::function<Value *()> &alternateGenerator) {
auto parentFunc = Builder.getInsertionBlock()->getParent();

PhiInst::ValueListType values;
PhiInst::BasicBlockListType blocks;

auto alternateBlock = Builder.createBasicBlock(parentFunc);
auto consequentBlock = Builder.createBasicBlock(parentFunc);
auto continueBlock = Builder.createBasicBlock(parentFunc);

Builder.createCondBranchInst(
conditionGenerator(), consequentBlock, alternateBlock);

Builder.setInsertionBlock(consequentBlock);
values.push_back(consequentGenerator());
blocks.push_back(Builder.getInsertionBlock());
Builder.createBranchInst(continueBlock);

Builder.setInsertionBlock(alternateBlock);
values.push_back(alternateGenerator());
blocks.push_back(Builder.getInsertionBlock());
Builder.createBranchInst(continueBlock);

Builder.setInsertionBlock(continueBlock);
return Builder.createPhiInst(values, blocks);
}

Value *ESTreeIRGen::genConditionalExpr(ESTree::ConditionalExpressionNode *C) {
auto parentFunc = Builder.getInsertionBlock()->getParent();

Expand Down Expand Up @@ -2389,7 +2442,7 @@ Value *ESTreeIRGen::genIdentifierExpression(

// For uses of undefined/Infinity/NaN as the global property, we make an
// optimization to always return the constant directly.
if (llvh::isa<GlobalObjectProperty>(Var)) {
if (llvh::isa<GlobalObjectProperty>(Var) && withScopes_.empty()) {
if (StrName.getUnderlyingPointer() == kw_.identUndefined) {
return Builder.getLiteralUndefined();
}
Expand All @@ -2406,7 +2459,7 @@ Value *ESTreeIRGen::genIdentifierExpression(
<< curFunction()->function->getInternalNameStr() << "\"\n");

// Typeof <variable> does not throw.
return emitLoad(Var, afterTypeOf);
return withAwareEmitLoad(Var, Iden, {}, afterTypeOf);
}

Value *ESTreeIRGen::genMetaProperty(ESTree::MetaPropertyNode *MP) {
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/ESTreeIRGen-stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ void ESTreeIRGen::genStatement(ESTree::Node *stmt) {
return genClassDeclaration(classDecl);
}

if (auto *withDecl = llvh::dyn_cast<ESTree::WithStatementNode>(stmt)) {
return genWithStatement(withDecl);
}

Builder.getModule()->getContext().getSourceErrorManager().error(
stmt->getSourceRange(), Twine("invalid statement encountered."));
}
Expand Down
163 changes: 163 additions & 0 deletions lib/IRGen/ESTreeIRGen-with.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include "ESTreeIRGen.h"

#include "hermes/IR/Instrs.h"
#include "llvh/ADT/SmallString.h"

namespace hermes {
namespace irgen {

void ESTreeIRGen::genWithStatement(ESTree::WithStatementNode *with) {
WithScopeInfo withScope{};
withScope.depth = semCtx_.getWithLexicalDepth(with);

withScope.object = Builder.createVariable(
curFunction()->curScope->getVariableScope(),
Builder
.getLiteralString(
"with" + std::to_string(withScope.depth))
->getValue(),
Type::createAnyType(),
true);
withScope.object->setIsConst(true);
emitStore(genExpression(with->_object), withScope.object, true);

withScopes_.push_back(withScope);
genStatement(with->_body);
withScopes_.pop_back();
}

Value *ESTreeIRGen::emitLoadOrStoreWithStatementImpl(
Value *value,
Value *ptr,
bool declInit_,
ESTree::IdentifierNode *id,
ConditionalChainType conditionalChainType,
bool inhibitThrow) {
uint32_t identifierDepth = INT_MAX;
std::string_view name;

identifierDepth = getIDDecl(id)->scope->depth;
name = id->_name->c_str();

auto compareDepth = [](uint32_t searching, const WithScopeInfo &scope) {
return scope.depth > searching;
};

auto it = std::upper_bound(
withScopes_.begin(), withScopes_.end(), identifierDepth, compareDepth);

return createConditionalChainImpl(
it,
withScopes_.end(),
ptr,
value,
declInit_,
name,
conditionalChainType,
inhibitThrow);
}

Value *ESTreeIRGen::createConditionalChainImpl(
std::vector<WithScopeInfo>::iterator begin,
std::vector<WithScopeInfo>::iterator end,
Value *ptr,
Value *value,
bool declInit_,
std::string_view name,
ConditionalChainType conditionalChainType,
bool inhibitThrow) {
if (begin == end) {
if (conditionalChainType ==
ConditionalChainType::OBJECT_ONLY_WITH_UNDEFINED_ALTERNATE) {
return Builder.getLiteralUndefined();
} else if (
conditionalChainType ==
ConditionalChainType::OBJECT_ONLY_WITH_GLOBAL_ALTERNATE) {
return Builder.getGlobalObject();
} else if (value) {
return emitStore(value, ptr, declInit_);
} else {
return emitLoad(ptr, inhibitThrow);
}
}

auto &current = *(end - 1);
auto conditionGenerator = [&]() -> Value * {
auto *wrapper = Builder.createLoadPropertyInst(
Builder.getGlobalObject(), "HermesWithInternal");
wrapper = Builder.createLoadPropertyInst(wrapper, "_containsField");

auto *call = Builder.createCallInst(
wrapper,
Builder.getLiteralUndefined(),
Builder.getLiteralUndefined(),
{emitLoad(current.object, false),
Builder.getLiteralString(name.data())});

return call;
};

auto consequentGenerator = [&]() -> Value * {
auto loadWithObj = emitLoad(current.object, false);

if (conditionalChainType == ConditionalChainType::MEMBER_EXPRESSION) {
if (value) {
return Builder.createStorePropertyInst(value, loadWithObj, name.data());
} else {
return Builder.createLoadPropertyInst(loadWithObj, name.data());
}
}
return loadWithObj;
};

auto alternateGenerator = [&]() -> Value * {
return createConditionalChainImpl(
begin, end - 1,
ptr,
value,
declInit_,
name,
conditionalChainType,
inhibitThrow);
};

return genConditionalExpr(
conditionGenerator, consequentGenerator, alternateGenerator);
}

Value *ESTreeIRGen::withAwareEmitLoad(
hermes::Value *ptr,
ESTree::Node *node,
ConditionalChainType conditionalChainType,
bool inhibitThrow) {
auto *identifier =
llvh::dyn_cast_or_null<ESTree::IdentifierNode>(node);
if (!identifier || withScopes_.empty()) {
if (!ptr)
return Builder.getLiteralUndefined();
return emitLoad(ptr, inhibitThrow);
}
return emitLoadOrStoreWithStatementImpl(
nullptr, ptr, false, identifier, conditionalChainType, inhibitThrow);
}

Value *ESTreeIRGen::withAwareEmitStore(
Value *storedValue,
Value *ptr,
bool declInit_,
ESTree::Node *node) {
auto *identifier =
llvh::dyn_cast_or_null<ESTree::IdentifierNode>(node);
if (!identifier || withScopes_.empty()) {
return emitStore(storedValue, ptr, declInit_);
}
return emitLoadOrStoreWithStatementImpl(
storedValue,
ptr,
declInit_,
identifier,
ConditionalChainType::MEMBER_EXPRESSION);
}

} // namespace irgen
} // namespace hermes
7 changes: 4 additions & 3 deletions lib/IRGen/ESTreeIRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ Value *LReference::emitLoad() {
llvh::cast<ESTree::MemberExpressionNode>(ast_), base_, property_)
.result;
case Kind::VarOrGlobal:
return irgen_->emitLoad(base_, false);
return irgen_->withAwareEmitLoad(
base_, ast_, ESTreeIRGen::ConditionalChainType::MEMBER_EXPRESSION);
case Kind::Destructuring:
assert(false && "destructuring cannot be loaded");
return builder.getLiteralUndefined();
Expand All @@ -76,7 +77,7 @@ void LReference::emitStore(Value *value) {
base_,
property_);
case Kind::VarOrGlobal:
irgen_->emitStore(value, base_, declInit_);
irgen_->withAwareEmitStore(value, base_, declInit_, ast_);
return;
case Kind::Error:
return;
Expand Down Expand Up @@ -414,7 +415,7 @@ LReference ESTreeIRGen::createLRef(ESTree::Node *node, bool declInit) {
LReference::Kind::VarOrGlobal,
this,
declInit,
nullptr,
iden,
var,
nullptr,
sourceLoc);
Expand Down
Loading

0 comments on commit 82eaf24

Please sign in to comment.