Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
133 changes: 133 additions & 0 deletions lib/IRGen/ESTreeIRGen-with.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#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();
}

template <typename Callback>
Value *ESTreeIRGen::createWithConditionalChain(
ESTree::Node *node,
const Callback &callback) {
ESTree::IdentifierNode *identifier = !withScopes_.empty() && node
? llvh::dyn_cast<ESTree::IdentifierNode>(node)
: nullptr;
if (!identifier) {
return callback(nullptr, "");
}

uint32_t identifierDepth = INT_MAX;
identifierDepth = getIDDecl(identifier)->scope->depth;
std::string_view name = identifier->_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 createWithConditionalChainImpl(it, withScopes_.end(), callback, name);
}

template <typename Callback>
Value *ESTreeIRGen::createWithConditionalChainImpl(
std::vector<WithScopeInfo>::iterator begin,
std::vector<WithScopeInfo>::iterator end,
const Callback &callback,
std::string_view name) {
if (begin == end) {
return callback(nullptr, name);
}

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);
return callback(loadWithObj, name);
};

auto alternateGenerator = [&]() -> Value * {
return createWithConditionalChainImpl(
begin, end - 1, callback, name);
};

return genConditionalExpr(
conditionGenerator, consequentGenerator, alternateGenerator);
}

Value *ESTreeIRGen::withAwareEmitLoad(
hermes::Value *ptr,
ESTree::Node *node,
ConditionalChainType conditionalChainType,
bool inhibitThrow) {
return createWithConditionalChain(
node, [&](Value *withObject, std::string_view idName) -> Value * {
switch (conditionalChainType) {
case ConditionalChainType::OBJECT_ONLY_WITH_UNDEFINED_ALTERNATE:
return withObject ? withObject : Builder.getLiteralUndefined();
case ConditionalChainType::OBJECT_ONLY_WITH_GLOBAL_ALTERNATE:
return withObject ? withObject : Builder.getGlobalObject();
case ConditionalChainType::MEMBER_EXPRESSION:
return withObject
? Builder.createLoadPropertyInst(withObject, idName.data())
: emitLoad(ptr, inhibitThrow);
default:
llvm_unreachable("Unhandled conditionalChainType");
}
});
}

void ESTreeIRGen::withAwareEmitStore(
Value *storedValue,
Value *ptr,
bool declInit_,
ESTree::Node *node) {
createWithConditionalChain(
node, [&](Value *withObject, std::string_view idName) {
if (!withObject)
emitStore(storedValue, ptr, declInit_);
else
Builder.createStorePropertyInst(
storedValue, withObject, idName.data());
return Builder.getLiteralUndefined();
});
}

} // 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