Reland "[esnext] re-implement template strings"
- Add a new bytecode for the ToString operation, replacing the old intrinsic call (currently does not collect type feedback). - Add a new AST node to represent TemplateLiterals, and avoid generating unnecessary ToString operations in some simple cases. - Use a single feedback slot for each string addition, because the type feedback should always be the same for each addition This seems to produce a very slight improvement on JSTests benchmarks and bench-ruben.js from v8:7415, and it's possible that type feedback for the ToString bytecode could provide more opportunities to eliminate the runtime call in TurboFan. Doesn't touch tagged templates [esnext] fix OOB read in ASTPrinter::VisistTemplateLiteral Fixes an error where TemplateLiteral printing in --print-ast would try to read an element beyond the length of a vector. BUG=v8:7415, chromium:820596 R=adamk@chromium.org, gsathya@chromum.org, rmcilroy@chromium.org, ishell@chromium.org, bmeurer@chromium.org Change-Id: Ie56894f73a6445550a5f95f42160c4e29ab1da42 Reviewed-on: https://chromium-review.googlesource.com/958408 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Caitlin Potter <caitp@igalia.com> Cr-Commit-Position: refs/heads/master@{#51933}
This commit is contained in:
parent
39bc48e283
commit
b8229612bf
@ -544,6 +544,15 @@ void AstTraversalVisitor<Subclass>::VisitGetTemplateObject(
|
||||
PROCESS_EXPRESSION(expr);
|
||||
}
|
||||
|
||||
template <class Subclass>
|
||||
void AstTraversalVisitor<Subclass>::VisitTemplateLiteral(
|
||||
TemplateLiteral* expr) {
|
||||
PROCESS_EXPRESSION(expr);
|
||||
for (Expression* sub : *expr->substitutions()) {
|
||||
RECURSE_EXPRESSION(Visit(sub));
|
||||
}
|
||||
}
|
||||
|
||||
template <class Subclass>
|
||||
void AstTraversalVisitor<Subclass>::VisitImportCallExpression(
|
||||
ImportCallExpression* expr) {
|
||||
|
@ -100,6 +100,7 @@ namespace internal {
|
||||
V(StoreInArrayLiteral) \
|
||||
V(SuperCallReference) \
|
||||
V(SuperPropertyReference) \
|
||||
V(TemplateLiteral) \
|
||||
V(ThisFunction) \
|
||||
V(Throw) \
|
||||
V(UnaryOperation) \
|
||||
@ -2677,6 +2678,26 @@ class GetTemplateObject final : public Expression {
|
||||
const ZoneList<const AstRawString*>* raw_strings_;
|
||||
};
|
||||
|
||||
class TemplateLiteral final : public Expression {
|
||||
public:
|
||||
using StringList = ZoneList<const AstRawString*>;
|
||||
using ExpressionList = ZoneList<Expression*>;
|
||||
|
||||
const StringList* string_parts() const { return string_parts_; }
|
||||
const ExpressionList* substitutions() const { return substitutions_; }
|
||||
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
TemplateLiteral(const StringList* parts, const ExpressionList* substitutions,
|
||||
int pos)
|
||||
: Expression(pos, kTemplateLiteral),
|
||||
string_parts_(parts),
|
||||
substitutions_(substitutions) {}
|
||||
|
||||
const StringList* string_parts_;
|
||||
const ExpressionList* substitutions_;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Basic visitor
|
||||
// Sub-class should parametrize AstVisitor with itself, e.g.:
|
||||
@ -3259,6 +3280,12 @@ class AstNodeFactory final BASE_EMBEDDED {
|
||||
return new (zone_) GetTemplateObject(cooked_strings, raw_strings, pos);
|
||||
}
|
||||
|
||||
TemplateLiteral* NewTemplateLiteral(
|
||||
const ZoneList<const AstRawString*>* string_parts,
|
||||
const ZoneList<Expression*>* substitutions, int pos) {
|
||||
return new (zone_) TemplateLiteral(string_parts, substitutions, pos);
|
||||
}
|
||||
|
||||
ImportCallExpression* NewImportCallExpression(Expression* args, int pos) {
|
||||
return new (zone_) ImportCallExpression(args, pos);
|
||||
}
|
||||
|
@ -471,6 +471,12 @@ void CallPrinter::VisitGetIterator(GetIterator* node) {
|
||||
|
||||
void CallPrinter::VisitGetTemplateObject(GetTemplateObject* node) {}
|
||||
|
||||
void CallPrinter::VisitTemplateLiteral(TemplateLiteral* node) {
|
||||
for (Expression* substitution : *node->substitutions()) {
|
||||
Find(substitution, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CallPrinter::VisitImportCallExpression(ImportCallExpression* node) {
|
||||
Print("ImportCall(");
|
||||
Find(node->argument(), true);
|
||||
@ -1343,6 +1349,19 @@ void AstPrinter::VisitGetTemplateObject(GetTemplateObject* node) {
|
||||
IndentedScope indent(this, "GET-TEMPLATE-OBJECT", node->position());
|
||||
}
|
||||
|
||||
void AstPrinter::VisitTemplateLiteral(TemplateLiteral* node) {
|
||||
IndentedScope indent(this, "TEMPLATE-LITERAL", node->position());
|
||||
const AstRawString* string = node->string_parts()->first();
|
||||
if (!string->IsEmpty()) PrintLiteralIndented("SPAN", string, true);
|
||||
for (int i = 0; i < node->substitutions()->length();) {
|
||||
PrintIndentedVisit("EXPR", node->substitutions()->at(i++));
|
||||
if (i < node->string_parts()->length()) {
|
||||
string = node->string_parts()->at(i);
|
||||
if (!string->IsEmpty()) PrintLiteralIndented("SPAN", string, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AstPrinter::VisitImportCallExpression(ImportCallExpression* node) {
|
||||
IndentedScope indent(this, "IMPORT-CALL", node->position());
|
||||
Visit(node->argument());
|
||||
|
@ -2531,6 +2531,12 @@ void BytecodeGraphBuilder::VisitToObject() {
|
||||
BuildCastOperator(javascript()->ToObject());
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitToString() {
|
||||
Node* value =
|
||||
NewNode(javascript()->ToString(), environment()->LookupAccumulator());
|
||||
environment()->BindAccumulator(value, Environment::kAttachFrameState);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitToNumber() {
|
||||
PrepareEagerCheckpoint();
|
||||
Node* object = environment()->LookupAccumulator();
|
||||
|
@ -549,6 +549,7 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
|
||||
case Bytecode::kToObject:
|
||||
case Bytecode::kToNumber:
|
||||
case Bytecode::kToName:
|
||||
case Bytecode::kToString:
|
||||
// Misc.
|
||||
case Bytecode::kForInEnumerate:
|
||||
case Bytecode::kForInPrepare:
|
||||
|
@ -1004,6 +1004,11 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::ToName(Register out) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::ToString() {
|
||||
OutputToString();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::ToNumber(int feedback_slot) {
|
||||
OutputToNumber(feedback_slot);
|
||||
return *this;
|
||||
|
@ -376,6 +376,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
|
||||
// Converts accumulator and stores result in register |out|.
|
||||
BytecodeArrayBuilder& ToObject(Register out);
|
||||
BytecodeArrayBuilder& ToName(Register out);
|
||||
BytecodeArrayBuilder& ToString();
|
||||
|
||||
// Converts accumulator and stores result back in accumulator.
|
||||
BytecodeArrayBuilder& ToNumber(int feedback_slot);
|
||||
|
@ -593,6 +593,11 @@ class BytecodeGenerator::ExpressionResultScope {
|
||||
type_hint_ = TypeHint::kBoolean;
|
||||
}
|
||||
|
||||
void SetResultIsString() {
|
||||
DCHECK_EQ(type_hint_, TypeHint::kAny);
|
||||
type_hint_ = TypeHint::kString;
|
||||
}
|
||||
|
||||
TypeHint type_hint() const { return type_hint_; }
|
||||
|
||||
private:
|
||||
@ -2057,6 +2062,7 @@ void BytecodeGenerator::VisitLiteral(Literal* expr) {
|
||||
break;
|
||||
case Literal::kString:
|
||||
builder()->LoadLiteral(expr->AsRawString());
|
||||
execution_result()->SetResultIsString();
|
||||
break;
|
||||
case Literal::kSymbol:
|
||||
builder()->LoadLiteral(expr->AsSymbol());
|
||||
@ -3995,13 +4001,23 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
|
||||
Expression* subexpr;
|
||||
Smi* literal;
|
||||
if (expr->IsSmiLiteralOperation(&subexpr, &literal)) {
|
||||
VisitForAccumulatorValue(subexpr);
|
||||
TypeHint type_hint = VisitForAccumulatorValue(subexpr);
|
||||
builder()->SetExpressionPosition(expr);
|
||||
builder()->BinaryOperationSmiLiteral(expr->op(), literal,
|
||||
feedback_index(slot));
|
||||
if (expr->op() == Token::ADD && type_hint == TypeHint::kString) {
|
||||
execution_result()->SetResultIsString();
|
||||
}
|
||||
} else {
|
||||
Register lhs = VisitForRegisterValue(expr->left());
|
||||
VisitForAccumulatorValue(expr->right());
|
||||
TypeHint lhs_type = VisitForAccumulatorValue(expr->left());
|
||||
Register lhs = register_allocator()->NewRegister();
|
||||
builder()->StoreAccumulatorInRegister(lhs);
|
||||
TypeHint rhs_type = VisitForAccumulatorValue(expr->right());
|
||||
if (expr->op() == Token::ADD &&
|
||||
(lhs_type == TypeHint::kString || rhs_type == TypeHint::kString)) {
|
||||
execution_result()->SetResultIsString();
|
||||
}
|
||||
|
||||
builder()->SetExpressionPosition(expr);
|
||||
builder()->BinaryOperation(expr->op(), lhs, feedback_index(slot));
|
||||
}
|
||||
@ -4009,7 +4025,7 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
|
||||
|
||||
void BytecodeGenerator::VisitNaryArithmeticExpression(NaryOperation* expr) {
|
||||
// TODO(leszeks): Add support for lhs smi in commutative ops.
|
||||
VisitForAccumulatorValue(expr->first());
|
||||
TypeHint type_hint = VisitForAccumulatorValue(expr->first());
|
||||
|
||||
for (size_t i = 0; i < expr->subsequent_length(); ++i) {
|
||||
RegisterAllocationScope register_scope(this);
|
||||
@ -4021,13 +4037,19 @@ void BytecodeGenerator::VisitNaryArithmeticExpression(NaryOperation* expr) {
|
||||
} else {
|
||||
Register lhs = register_allocator()->NewRegister();
|
||||
builder()->StoreAccumulatorInRegister(lhs);
|
||||
VisitForAccumulatorValue(expr->subsequent(i));
|
||||
TypeHint rhs_hint = VisitForAccumulatorValue(expr->subsequent(i));
|
||||
if (rhs_hint == TypeHint::kString) type_hint = TypeHint::kString;
|
||||
builder()->SetExpressionPosition(expr->subsequent_op_position(i));
|
||||
builder()->BinaryOperation(
|
||||
expr->op(), lhs,
|
||||
feedback_index(feedback_spec()->AddBinaryOpICSlot()));
|
||||
}
|
||||
}
|
||||
|
||||
if (type_hint == TypeHint::kString && expr->op() == Token::ADD) {
|
||||
// If any operand of an ADD is a String, a String is produced.
|
||||
execution_result()->SetResultIsString();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: the actual spreading is performed by the surrounding expression's
|
||||
@ -4210,6 +4232,53 @@ void BytecodeGenerator::VisitGetTemplateObject(GetTemplateObject* expr) {
|
||||
builder()->GetTemplateObject(entry, feedback_index(literal_slot));
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitTemplateLiteral(TemplateLiteral* expr) {
|
||||
const TemplateLiteral::StringList& parts = *expr->string_parts();
|
||||
const TemplateLiteral::ExpressionList& substitutions = *expr->substitutions();
|
||||
// Template strings with no substitutions are turned into StringLiterals.
|
||||
DCHECK_GT(substitutions.length(), 0);
|
||||
DCHECK_EQ(parts.length(), substitutions.length() + 1);
|
||||
|
||||
// Generate string concatenation
|
||||
// TODO(caitp): Don't generate feedback slot if it's not used --- introduce
|
||||
// a simple, concise, reusable mechanism to lazily create reusable slots.
|
||||
FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
|
||||
Register last_part = register_allocator()->NewRegister();
|
||||
bool last_part_valid = false;
|
||||
|
||||
builder()->SetExpressionPosition(expr);
|
||||
for (int i = 0; i < substitutions.length(); ++i) {
|
||||
if (i != 0) {
|
||||
builder()->StoreAccumulatorInRegister(last_part);
|
||||
last_part_valid = true;
|
||||
}
|
||||
|
||||
if (!parts[i]->IsEmpty()) {
|
||||
builder()->LoadLiteral(parts[i]);
|
||||
if (last_part_valid) {
|
||||
builder()->BinaryOperation(Token::ADD, last_part, feedback_index(slot));
|
||||
}
|
||||
builder()->StoreAccumulatorInRegister(last_part);
|
||||
last_part_valid = true;
|
||||
}
|
||||
|
||||
TypeHint type_hint = VisitForAccumulatorValue(substitutions[i]);
|
||||
if (type_hint != TypeHint::kString) {
|
||||
builder()->ToString();
|
||||
}
|
||||
if (last_part_valid) {
|
||||
builder()->BinaryOperation(Token::ADD, last_part, feedback_index(slot));
|
||||
}
|
||||
last_part_valid = false;
|
||||
}
|
||||
|
||||
if (!parts.last()->IsEmpty()) {
|
||||
builder()->StoreAccumulatorInRegister(last_part);
|
||||
builder()->LoadLiteral(parts.last());
|
||||
builder()->BinaryOperation(Token::ADD, last_part, feedback_index(slot));
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
||||
builder()->LoadAccumulatorWithRegister(Register::function_closure());
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
|
||||
|
||||
enum class TestFallthrough { kThen, kElse, kNone };
|
||||
enum class TypeHint { kAny, kBoolean };
|
||||
enum class TypeHint { kAny, kBoolean, kString };
|
||||
|
||||
void GenerateBytecodeBody();
|
||||
void AllocateDeferredConstants(Isolate* isolate, Handle<Script> script);
|
||||
|
@ -223,6 +223,7 @@ namespace interpreter {
|
||||
V(ToNumber, AccumulatorUse::kReadWrite, OperandType::kIdx) \
|
||||
V(ToNumeric, AccumulatorUse::kReadWrite, OperandType::kIdx) \
|
||||
V(ToObject, AccumulatorUse::kRead, OperandType::kRegOut) \
|
||||
V(ToString, AccumulatorUse::kReadWrite) \
|
||||
\
|
||||
/* Literals */ \
|
||||
V(CreateRegExpLiteral, AccumulatorUse::kWrite, OperandType::kIdx, \
|
||||
|
@ -1304,6 +1304,14 @@ IGNITION_HANDLER(ToObject, InterpreterAssembler) {
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
// ToString
|
||||
//
|
||||
// Convert the accumulator to a String.
|
||||
IGNITION_HANDLER(ToString, InterpreterAssembler) {
|
||||
SetAccumulator(ToString_Inline(GetContext(), GetAccumulator()));
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
class IncDecAssembler : public UnaryNumericOpAssembler {
|
||||
public:
|
||||
explicit IncDecAssembler(CodeAssemblerState* state, Bytecode bytecode,
|
||||
|
@ -3492,52 +3492,10 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
|
||||
DCHECK_EQ(cooked_strings->length(), expressions->length() + 1);
|
||||
|
||||
if (!tag) {
|
||||
const AstRawString* first_string = cooked_strings->at(0);
|
||||
if (expressions->length() == 0) {
|
||||
return factory()->NewStringLiteral(first_string, kNoSourcePosition);
|
||||
if (cooked_strings->length() == 1) {
|
||||
return factory()->NewStringLiteral(cooked_strings->first(), pos);
|
||||
}
|
||||
|
||||
size_t num_empty =
|
||||
std::count_if(cooked_strings->begin(), cooked_strings->end(),
|
||||
[=](const AstRawString* lit) { return lit->IsEmpty(); });
|
||||
|
||||
const bool kFirstIsEmpty = first_string->IsEmpty();
|
||||
Expression* first = kFirstIsEmpty ? ToString(expressions->at(0))
|
||||
: factory()->NewStringLiteral(
|
||||
first_string, kNoSourcePosition);
|
||||
|
||||
// Build N-ary addition op to simplify code-generation.
|
||||
// TODO(leszeks): Could we just store this expression in the
|
||||
// TemplateLiteralState and build it as we go?
|
||||
NaryOperation* expr = factory()->NewNaryOperation(
|
||||
Token::ADD, first, 2 * expressions->length() - num_empty);
|
||||
|
||||
int i = 0;
|
||||
if (kFirstIsEmpty) {
|
||||
// If the first string is empty, possibly add the next template span
|
||||
// outside of the loop, to keep the loop logic simple.
|
||||
i = 1;
|
||||
const AstRawString* str = cooked_strings->at(1);
|
||||
if (!str->IsEmpty()) {
|
||||
expr->AddSubsequent(factory()->NewStringLiteral(str, kNoSourcePosition),
|
||||
first->position());
|
||||
}
|
||||
}
|
||||
|
||||
while (i < expressions->length()) {
|
||||
Expression* sub = expressions->at(i++);
|
||||
const AstRawString* cooked_str = cooked_strings->at(i);
|
||||
DCHECK_NOT_NULL(cooked_str);
|
||||
|
||||
// Let middle be ToString(sub).
|
||||
expr->AddSubsequent(ToString(sub), sub->position());
|
||||
if (!cooked_str->IsEmpty()) {
|
||||
expr->AddSubsequent(
|
||||
factory()->NewStringLiteral(cooked_str, kNoSourcePosition),
|
||||
sub->position());
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
return factory()->NewTemplateLiteral(cooked_strings, expressions, pos);
|
||||
} else {
|
||||
// GetTemplateObject
|
||||
Expression* template_object =
|
||||
|
@ -787,6 +787,7 @@ NOT_A_PATTERN(StoreInArrayLiteral)
|
||||
NOT_A_PATTERN(SuperPropertyReference)
|
||||
NOT_A_PATTERN(SuperCallReference)
|
||||
NOT_A_PATTERN(SwitchStatement)
|
||||
NOT_A_PATTERN(TemplateLiteral)
|
||||
NOT_A_PATTERN(ThisFunction)
|
||||
NOT_A_PATTERN(Throw)
|
||||
NOT_A_PATTERN(TryCatchStatement)
|
||||
|
@ -13,20 +13,22 @@ snippet: "
|
||||
"
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 30
|
||||
bytecode array length: 28
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaSmi), I8(1),
|
||||
B(Star), R(0),
|
||||
/* 53 S> */ B(LdaSmi), I8(2),
|
||||
B(Star), R(1),
|
||||
/* 56 S> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(0), U8(1),
|
||||
/* 56 S> */ B(Ldar), R(0),
|
||||
B(ToString),
|
||||
B(Star), R(2),
|
||||
/* 70 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(1), U8(1),
|
||||
/* 70 E> */ B(Add), R(2), U8(0),
|
||||
B(Ldar), R(1),
|
||||
/* 70 E> */ B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
/* 70 E> */ B(Add), R(2), U8(1),
|
||||
B(Add), R(2), U8(0),
|
||||
/* 80 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -43,7 +45,7 @@ snippet: "
|
||||
"
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 30
|
||||
bytecode array length: 28
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaSmi), I8(1),
|
||||
@ -52,11 +54,13 @@ bytecodes: [
|
||||
B(Star), R(1),
|
||||
/* 56 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(2),
|
||||
/* 72 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(0), U8(1),
|
||||
/* 72 E> */ B(Add), R(2), U8(0),
|
||||
B(Ldar), R(0),
|
||||
/* 72 E> */ B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
/* 76 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(1), U8(1),
|
||||
/* 76 E> */ B(Add), R(2), U8(1),
|
||||
B(Ldar), R(1),
|
||||
/* 76 E> */ B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
/* 80 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -73,20 +77,22 @@ snippet: "
|
||||
"
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 30
|
||||
bytecode array length: 28
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaSmi), I8(1),
|
||||
B(Star), R(0),
|
||||
/* 53 S> */ B(LdaSmi), I8(2),
|
||||
B(Star), R(1),
|
||||
/* 56 S> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(0), U8(1),
|
||||
/* 56 S> */ B(Ldar), R(0),
|
||||
B(ToString),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
/* 66 E> */ B(Add), R(2), U8(0),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
/* 76 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(1), U8(1),
|
||||
/* 76 E> */ B(Add), R(2), U8(1),
|
||||
B(Ldar), R(1),
|
||||
/* 76 E> */ B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
/* 80 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -101,9 +107,9 @@ snippet: "
|
||||
var b = 2;
|
||||
return `foo${a}bar${b}baz${1}`;
|
||||
"
|
||||
frame size: 4
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 57
|
||||
bytecode array length: 50
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaSmi), I8(1),
|
||||
@ -112,22 +118,23 @@ bytecodes: [
|
||||
B(Star), R(1),
|
||||
/* 56 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(2),
|
||||
/* 69 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(0), U8(1),
|
||||
/* 69 E> */ B(Add), R(2), U8(0),
|
||||
B(Ldar), R(0),
|
||||
/* 69 E> */ B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(1),
|
||||
/* 69 E> */ B(Add), R(2), U8(1),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
/* 76 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(1), U8(1),
|
||||
/* 76 E> */ B(Add), R(2), U8(2),
|
||||
B(Ldar), R(1),
|
||||
/* 76 E> */ B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(2),
|
||||
/* 76 E> */ B(Add), R(2), U8(3),
|
||||
B(Add), R(2), U8(0),
|
||||
B(Star), R(2),
|
||||
B(LdaSmi), I8(1),
|
||||
B(Star), R(3),
|
||||
B(InvokeIntrinsic), U8(Runtime::k_ToString), R(3), U8(1),
|
||||
/* 83 E> */ B(Add), R(2), U8(4),
|
||||
B(ToString),
|
||||
B(Add), R(2), U8(0),
|
||||
/* 87 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -146,23 +153,25 @@ snippet: "
|
||||
"
|
||||
frame size: 4
|
||||
parameter count: 1
|
||||
bytecode array length: 37
|
||||
bytecode array length: 35
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaSmi), I8(1),
|
||||
B(Star), R(0),
|
||||
/* 53 S> */ B(LdaSmi), I8(2),
|
||||
B(Star), R(1),
|
||||
/* 56 S> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(0), U8(1),
|
||||
/* 56 S> */ B(Ldar), R(0),
|
||||
B(ToString),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
/* 66 E> */ B(Add), R(2), U8(0),
|
||||
B(Add), R(2), U8(1),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
/* 87 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(1), U8(1),
|
||||
/* 87 E> */ B(Add), R(3), U8(1),
|
||||
/* 76 E> */ B(Add), R(2), U8(2),
|
||||
B(Ldar), R(1),
|
||||
/* 87 E> */ B(ToString),
|
||||
B(Add), R(3), U8(2),
|
||||
/* 76 E> */ B(Add), R(2), U8(0),
|
||||
/* 91 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -178,9 +187,9 @@ snippet: "
|
||||
function foo(a, b) { };
|
||||
return `string${foo(a, b)}${a}${b}`;
|
||||
"
|
||||
frame size: 5
|
||||
frame size: 4
|
||||
parameter count: 1
|
||||
bytecode array length: 52
|
||||
bytecode array length: 45
|
||||
bytecodes: [
|
||||
B(CreateClosure), U8(0), U8(0), U8(2),
|
||||
B(Star), R(2),
|
||||
@ -191,16 +200,17 @@ bytecodes: [
|
||||
B(Star), R(1),
|
||||
/* 80 S> */ B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
/* 96 E> */ B(CallUndefinedReceiver2), R(2), R(0), R(1), U8(1),
|
||||
B(Star), R(4),
|
||||
B(InvokeIntrinsic), U8(Runtime::k_ToString), R(4), U8(1),
|
||||
/* 96 E> */ B(Add), R(3), U8(3),
|
||||
/* 96 E> */ B(CallUndefinedReceiver2), R(2), R(0), R(1), U8(2),
|
||||
B(ToString),
|
||||
B(Add), R(3), U8(1),
|
||||
B(Star), R(3),
|
||||
/* 108 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(0), U8(1),
|
||||
/* 108 E> */ B(Add), R(3), U8(4),
|
||||
B(Ldar), R(0),
|
||||
/* 108 E> */ B(ToString),
|
||||
B(Add), R(3), U8(1),
|
||||
B(Star), R(3),
|
||||
/* 112 E> */ B(InvokeIntrinsic), U8(Runtime::k_ToString), R(1), U8(1),
|
||||
/* 112 E> */ B(Add), R(3), U8(5),
|
||||
B(Ldar), R(1),
|
||||
/* 112 E> */ B(ToString),
|
||||
B(Add), R(3), U8(1),
|
||||
/* 116 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
8
test/mjsunit/es6/regress/regress-crbug-820596.js
Normal file
8
test/mjsunit/es6/regress/regress-crbug-820596.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2018 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --print-ast
|
||||
|
||||
var x;
|
||||
`Crashes if OOB read with --print-ast ${x}`;
|
@ -262,7 +262,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
.CompareNull();
|
||||
|
||||
// Emit conversion operator invocations.
|
||||
builder.ToNumber(1).ToNumeric(1).ToObject(reg).ToName(reg);
|
||||
builder.ToNumber(1).ToNumeric(1).ToObject(reg).ToName(reg).ToString();
|
||||
|
||||
// Emit GetSuperConstructor.
|
||||
builder.GetSuperConstructor(reg);
|
||||
|
Loading…
Reference in New Issue
Block a user