Commit b6596aa7 authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[es7] implement |do| expressions proposal

Adds an implementation of "do expression" parsing (https://webcache.googleusercontent.com/search?q=cache:MIGALjqPDNgJ:wiki.ecmascript.org/doku.php%3Fid%3Dstrawman:do_expressions+&cd=1&hl=en&ct=clnk&gl=us).

This feature provides a way to evaluate a block of statements within an expression context, producing the resulting completion value. This is very helpful for implementing certain language features via desugaring.

BUG=v8:4488
LOG=N
R=adamk@chromium.org, bmeurer@chromium.org, rossberg@chromium.org, wingo@igalia.com

Review URL: https://codereview.chromium.org/1399893002

Cr-Commit-Position: refs/heads/master@{#31428}
parent 5c1b11b5
......@@ -196,6 +196,12 @@ void AstExpressionVisitor::VisitNativeFunctionLiteral(
NativeFunctionLiteral* expr) {}
void AstExpressionVisitor::VisitDoExpression(DoExpression* expr) {
RECURSE(VisitBlock(expr->block()));
RECURSE(VisitVariableProxy(expr->result()));
}
void AstExpressionVisitor::VisitConditional(Conditional* expr) {
RECURSE(Visit(expr->condition()));
RECURSE(Visit(expr->then_expression()));
......
......@@ -43,6 +43,11 @@ void AstLiteralReindexer::VisitNativeFunctionLiteral(
NativeFunctionLiteral* node) {}
void AstLiteralReindexer::VisitDoExpression(DoExpression* node) {
// TODO(caitp): literals in do expressions need re-indexing too.
}
void AstLiteralReindexer::VisitLiteral(Literal* node) {}
......
......@@ -136,6 +136,15 @@ void AstNumberingVisitor::VisitNativeFunctionLiteral(
}
void AstNumberingVisitor::VisitDoExpression(DoExpression* node) {
IncrementNodeCount();
DisableCrankshaft(kDoExpression);
node->set_base_id(ReserveIdRange(DoExpression::num_ids()));
Visit(node->block());
Visit(node->result());
}
void AstNumberingVisitor::VisitLiteral(Literal* node) {
IncrementNodeCount();
node->set_base_id(ReserveIdRange(Literal::num_ids()));
......
......@@ -90,7 +90,8 @@ namespace internal {
V(SuperPropertyReference) \
V(SuperCallReference) \
V(CaseClause) \
V(EmptyParentheses)
V(EmptyParentheses) \
V(DoExpression)
#define AST_NODE_LIST(V) \
DECLARATION_NODE_LIST(V) \
......@@ -490,6 +491,29 @@ class Block final : public BreakableStatement {
};
class DoExpression final : public Expression {
public:
DECLARE_NODE_TYPE(DoExpression)
Block* block() { return block_; }
VariableProxy* result() { return result_; }
protected:
DoExpression(Zone* zone, Block* block, VariableProxy* result, int pos)
: Expression(zone, pos), block_(block), result_(result) {
DCHECK_NOT_NULL(block_);
DCHECK_NOT_NULL(result_);
}
static int parent_num_ids() { return Expression::num_ids(); }
private:
int local_id(int n) const { return base_id() + parent_num_ids() + n; }
Block* block_;
VariableProxy* result_;
};
class Declaration : public AstNode {
public:
VariableProxy* proxy() const { return proxy_; }
......@@ -3195,6 +3219,11 @@ class AstVisitor BASE_EMBEDDED {
stack_overflow_ = false; \
} \
\
void InitializeAstVisitor(uintptr_t stack_limit) { \
stack_limit_ = stack_limit; \
stack_overflow_ = false; \
} \
\
uintptr_t stack_limit_; \
bool stack_overflow_
......@@ -3581,6 +3610,11 @@ class AstNodeFactory final BASE_EMBEDDED {
NativeFunctionLiteral(parser_zone_, name, extension, pos);
}
DoExpression* NewDoExpression(Block* block, Variable* result_var, int pos) {
VariableProxy* result = NewVariableProxy(result_var, pos);
return new (parser_zone_) DoExpression(parser_zone_, block, result, pos);
}
ThisFunction* NewThisFunction(int pos) {
return new (local_zone_) ThisFunction(local_zone_, pos);
}
......
......@@ -57,6 +57,7 @@ namespace internal {
V(kDestinationOfCopyNotAligned, "Destination of copy not aligned") \
V(kDontDeleteCellsCannotContainTheHole, \
"DontDelete cells can't contain the hole") \
V(kDoExpression, "Do expression encountered") \
V(kDoPushArgumentNotImplementedForDoubleType, \
"DoPushArgument not implemented for double type") \
V(kEliminatedBoundsCheckFailed, "Eliminated bounds check failed") \
......
......@@ -2161,6 +2161,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_completion)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tolength)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions)
static void SimpleInstallFunction(Handle<JSObject>& base, const char* name,
......@@ -2632,6 +2633,7 @@ bool Genesis::InstallExperimentalNatives() {
nullptr};
static const char* harmony_tolength_natives[] = {nullptr};
static const char* harmony_completion_natives[] = {nullptr};
static const char* harmony_do_expressions_natives[] = {nullptr};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
......
......@@ -1657,6 +1657,12 @@ void AstGraphBuilder::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
}
void AstGraphBuilder::VisitDoExpression(DoExpression* expr) {
VisitBlock(expr->block());
VisitVariableProxy(expr->result());
}
void AstGraphBuilder::VisitConditional(Conditional* expr) {
IfBuilder compare_if(this);
VisitForTest(expr->condition());
......
......@@ -77,6 +77,12 @@ void ALAA::VisitSuperCallReference(SuperCallReference* leaf) {}
void ALAA::VisitBlock(Block* stmt) { VisitStatements(stmt->statements()); }
void ALAA::VisitDoExpression(DoExpression* expr) {
Visit(expr->block());
Visit(expr->result());
}
void ALAA::VisitExpressionStatement(ExpressionStatement* stmt) {
Visit(stmt->expression());
}
......
......@@ -5535,6 +5535,11 @@ void HOptimizedGraphBuilder::VisitNativeFunctionLiteral(
}
void HOptimizedGraphBuilder::VisitDoExpression(DoExpression* expr) {
UNREACHABLE();
}
void HOptimizedGraphBuilder::VisitConditional(Conditional* expr) {
DCHECK(!HasStackOverflow());
DCHECK(current_block() != NULL);
......
......@@ -350,6 +350,13 @@ void AstTyper::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
}
void AstTyper::VisitDoExpression(DoExpression* expr) {
RECURSE(VisitBlock(expr->block()));
RECURSE(VisitVariableProxy(expr->result()));
NarrowType(expr, expr->result()->bounds());
}
void AstTyper::VisitConditional(Conditional* expr) {
// Collect type feedback.
expr->condition()->RecordToBooleanTypeFeedback(oracle());
......
......@@ -199,7 +199,8 @@ DEFINE_BOOL(legacy_const, true, "legacy semantics for const in sloppy mode")
V(harmony_destructuring, "harmony destructuring") \
V(harmony_default_parameters, "harmony default parameters") \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd")
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
......
......@@ -802,6 +802,15 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
}
void FullCodeGenerator::VisitDoExpression(DoExpression* expr) {
Comment cmnt(masm_, "[ Do Expression");
NestedStatement nested_block(this);
SetExpressionPosition(expr);
VisitBlock(expr->block());
EmitVariableLoad(expr->result());
}
void FullCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
Comment cmnt(masm_, "[ ExpressionStatement");
SetStatementPosition(stmt);
......
......@@ -514,6 +514,11 @@ void BytecodeGenerator::VisitNativeFunctionLiteral(
}
void BytecodeGenerator::VisitDoExpression(DoExpression* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitConditional(Conditional* expr) { UNIMPLEMENTED(); }
......
......@@ -15,6 +15,7 @@
#include "src/compiler.h"
#include "src/messages.h"
#include "src/preparser.h"
#include "src/rewriter.h"
#include "src/runtime/runtime.h"
#include "src/scanner-character-streams.h"
#include "src/scopeinfo.h"
......@@ -924,6 +925,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_new_target(FLAG_harmony_new_target);
set_allow_strong_mode(FLAG_strong_mode);
set_allow_legacy_const(FLAG_legacy_const);
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -4053,6 +4055,24 @@ void ParserTraits::ParseArrowFunctionFormalParameters(
}
DoExpression* Parser::ParseDoExpression(bool* ok) {
// AssignmentExpression ::
// do '{' StatementList '}'
int pos = peek_position();
Expect(Token::DO, CHECK_OK);
Variable* result =
scope_->NewTemporary(ast_value_factory()->dot_result_string());
Block* block = ParseScopedBlock(nullptr, CHECK_OK);
DoExpression* expr = factory()->NewDoExpression(block, result, pos);
if (!Rewriter::Rewrite(this, expr, ast_value_factory())) {
*ok = false;
return nullptr;
}
return expr;
}
void ParserTraits::ParseArrowFunctionFormalParameterList(
ParserFormalParameters* parameters, Expression* expr,
const Scanner::Location& params_loc,
......@@ -4792,6 +4812,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
SET_ALLOW(harmony_spread_arrays);
SET_ALLOW(harmony_new_target);
SET_ALLOW(strong_mode);
SET_ALLOW(harmony_do_expressions);
#undef SET_ALLOW
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
......
......@@ -796,6 +796,8 @@ class ParserTraits {
const Scanner::Location& params_loc,
Scanner::Location* duplicate_loc, bool* ok);
V8_INLINE DoExpression* ParseDoExpression(bool* ok);
void ReindexLiterals(const ParserFormalParameters& parameters);
// Temporary glue; these functions will move to ParserBase.
......@@ -971,6 +973,7 @@ class Parser : public ParserBase<ParserTraits> {
Block* ParseVariableStatement(VariableDeclarationContext var_context,
ZoneList<const AstRawString*>* names,
bool* ok);
DoExpression* ParseDoExpression(bool* ok);
struct DeclarationDescriptor {
enum Kind { NORMAL, PARAMETER };
......@@ -1104,6 +1107,8 @@ class Parser : public ParserBase<ParserTraits> {
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
Statement* body, bool* ok);
void RewriteDoExpression(Expression* expr, bool* ok);
FunctionLiteral* ParseFunctionLiteral(
const AstRawString* name, Scanner::Location function_name_location,
FunctionNameValidity function_name_validity, FunctionKind kind,
......@@ -1248,6 +1253,7 @@ ZoneList<Statement*>* ParserTraits::ParseEagerFunctionBody(
function_type, ok);
}
void ParserTraits::CheckConflictingVarDeclarations(v8::internal::Scope* scope,
bool* ok) {
parser_->CheckConflictingVarDeclarations(scope, ok);
......@@ -1377,6 +1383,13 @@ void ParserTraits::AddParameterInitializationBlock(
}
}
}
DoExpression* ParserTraits::ParseDoExpression(bool* ok) {
return parser_->ParseDoExpression(ok);
}
} // namespace internal
} // namespace v8
......
......@@ -388,6 +388,7 @@ NOT_A_PATTERN(Conditional)
NOT_A_PATTERN(ContinueStatement)
NOT_A_PATTERN(CountOperation)
NOT_A_PATTERN(DebuggerStatement)
NOT_A_PATTERN(DoExpression)
NOT_A_PATTERN(DoWhileStatement)
NOT_A_PATTERN(EmptyStatement)
NOT_A_PATTERN(EmptyParentheses)
......
......@@ -1239,6 +1239,23 @@ PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
return Expression::Default();
}
PreParserExpression PreParser::ParseDoExpression(bool* ok) {
// AssignmentExpression ::
// do '{' StatementList '}'
Expect(Token::DO, CHECK_OK);
Expect(Token::LBRACE, CHECK_OK);
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
{
BlockState block_state(&scope_, block_scope);
while (peek() != Token::RBRACE) {
ParseStatementListItem(CHECK_OK);
}
Expect(Token::RBRACE, CHECK_OK);
return PreParserExpression::Default();
}
}
#undef CHECK_OK
......
......@@ -88,6 +88,7 @@ class ParserBase : public Traits {
typedef typename Traits::Type::FunctionLiteral FunctionLiteralT;
typedef typename Traits::Type::Literal LiteralT;
typedef typename Traits::Type::ObjectLiteralProperty ObjectLiteralPropertyT;
typedef typename Traits::Type::StatementList StatementListT;
ParserBase(Zone* zone, Scanner* scanner, uintptr_t stack_limit,
v8::Extension* extension, AstValueFactory* ast_value_factory,
......@@ -117,7 +118,8 @@ class ParserBase : public Traits {
allow_harmony_spread_arrays_(false),
allow_harmony_new_target_(false),
allow_strong_mode_(false),
allow_legacy_const_(true) {}
allow_legacy_const_(true),
allow_harmony_do_expressions_(false) {}
#define ALLOW_ACCESSORS(name) \
bool allow_##name() const { return allow_##name##_; } \
......@@ -136,8 +138,11 @@ class ParserBase : public Traits {
ALLOW_ACCESSORS(harmony_new_target);
ALLOW_ACCESSORS(strong_mode);
ALLOW_ACCESSORS(legacy_const);
ALLOW_ACCESSORS(harmony_do_expressions);
#undef ALLOW_ACCESSORS
uintptr_t stack_limit() const { return stack_limit_; }
protected:
enum AllowRestrictedIdentifiers {
kAllowRestrictedIdentifiers,
......@@ -842,6 +847,7 @@ class ParserBase : public Traits {
bool allow_harmony_new_target_;
bool allow_strong_mode_;
bool allow_legacy_const_;
bool allow_harmony_do_expressions_;
};
......@@ -1698,6 +1704,7 @@ class PreParserTraits {
// Temporary glue; these functions will move to ParserBase.
PreParserExpression ParseV8Intrinsic(bool* ok);
V8_INLINE PreParserExpression ParseDoExpression(bool* ok);
PreParserExpression ParseFunctionLiteral(
PreParserIdentifier name, Scanner::Location function_name_location,
FunctionNameValidity function_name_validity, FunctionKind kind,
......@@ -1838,6 +1845,7 @@ class PreParser : public ParserBase<PreParserTraits> {
Expression ParseConditionalExpression(bool accept_IN, bool* ok);
Expression ParseObjectLiteral(bool* ok);
Expression ParseV8Intrinsic(bool* ok);
Expression ParseDoExpression(bool* ok);
V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count, bool* ok);
......@@ -1903,6 +1911,11 @@ void PreParserTraits::ParseArrowFunctionFormalParameterList(
}
PreParserExpression PreParserTraits::ParseDoExpression(bool* ok) {
return pre_parser_->ParseDoExpression(ok);
}
PreParserStatementList PreParser::ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos,
const PreParserFormalParameters& parameters, FunctionKind kind,
......@@ -2223,6 +2236,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
// ClassLiteral
// '(' Expression ')'
// TemplateLiteral
// do Block
int beg_pos = scanner()->peek_location().beg_pos;
int end_pos = scanner()->peek_location().end_pos;
......@@ -2403,6 +2417,15 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
result = this->ParseV8Intrinsic(CHECK_OK);
break;
}
case Token::DO:
// TODO(caitp): reorganize ParsePrimaryExpression() to not require this
// extra `token == Token::DO` test due to potential fall-through
if (token == Token::DO && allow_harmony_do_expressions()) {
BindingPatternUnexpectedToken(classifier);
result = Traits::ParseDoExpression(CHECK_OK);
break;
}
// If we're not allowing special syntax we fall-through to the
// default case.
......
......@@ -228,6 +228,9 @@ void CallPrinter::VisitClassLiteral(ClassLiteral* node) {
void CallPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {}
void CallPrinter::VisitDoExpression(DoExpression* node) { Find(node->block()); }
void CallPrinter::VisitConditional(Conditional* node) {
Find(node->condition());
Find(node->then_expression());
......@@ -701,6 +704,13 @@ void PrettyPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {
}
void PrettyPrinter::VisitDoExpression(DoExpression* node) {
Print("(do {");
PrintStatements(node->block()->statements());
Print("})");
}
void PrettyPrinter::VisitConditional(Conditional* node) {
Visit(node->condition());
Print(" ? ");
......@@ -1425,6 +1435,12 @@ void AstPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {
}
void AstPrinter::VisitDoExpression(DoExpression* node) {
IndentedScope indent(this, "DO EXPRESSION", node->position());
PrintStatements(node->block()->statements());
}
void AstPrinter::VisitConditional(Conditional* node) {
IndentedScope indent(this, "CONDITIONAL", node->position());
PrintIndentedVisit("CONDITION", node->condition());
......
......@@ -25,6 +25,17 @@ class Processor: public AstVisitor {
InitializeAstVisitor(isolate);
}
Processor(Parser* parser, Scope* scope, Variable* result,
AstValueFactory* ast_value_factory)
: result_(result),
result_assigned_(false),
replacement_(nullptr),
is_set_(false),
scope_(scope),
factory_(ast_value_factory) {
InitializeAstVisitor(parser->stack_limit());
}
virtual ~Processor() { }
void Process(ZoneList<Statement*>* statements);
......@@ -34,6 +45,17 @@ class Processor: public AstVisitor {
Scope* scope() { return scope_; }
AstNodeFactory* factory() { return &factory_; }
// Returns ".result = value"
Expression* SetResult(Expression* value) {
result_assigned_ = true;
VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
return factory()->NewAssignment(Token::ASSIGN, result_proxy, value,
RelocInfo::kNoPosition);
}
// Inserts '.result = undefined' in front of the given statement.
Statement* AssignUndefinedBefore(Statement* s);
private:
Variable* result_;
......@@ -57,17 +79,6 @@ class Processor: public AstVisitor {
Scope* scope_;
AstNodeFactory factory_;
// Returns ".result = value"
Expression* SetResult(Expression* value) {
result_assigned_ = true;
VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
return factory()->NewAssignment(
Token::ASSIGN, result_proxy, value, RelocInfo::kNoPosition);
}
// Inserts '.result = undefined' in front of the given statement.
Statement* AssignUndefinedBefore(Statement* s);
// Node visitors.
#define DEF_VISIT(type) virtual void Visit##type(type* node) override;
AST_NODE_LIST(DEF_VISIT)
......@@ -362,5 +373,31 @@ bool Rewriter::Rewrite(ParseInfo* info) {
}
bool Rewriter::Rewrite(Parser* parser, DoExpression* expr,
AstValueFactory* factory) {
Block* block = expr->block();
Scope* scope = block->scope();
ZoneList<Statement*>* body = block->statements();
VariableProxy* result = expr->result();
Variable* result_var = result->var();
if (!body->is_empty()) {
Processor processor(parser, scope, result_var, factory);
processor.Process(body);
if (processor.HasStackOverflow()) return false;
if (!processor.result_assigned()) {
AstNodeFactory* node_factory = processor.factory();
Expression* undef =
node_factory->NewUndefinedLiteral(RelocInfo::kNoPosition);
Statement* completion = node_factory->NewExpressionStatement(
processor.SetResult(undef), expr->position());
body->Add(completion, factory->zone());
}
}
return true;
}
} // namespace internal
} // namespace v8
......@@ -8,7 +8,10 @@
namespace v8 {
namespace internal {
class AstValueFactory;
class DoExpression;
class ParseInfo;
class Parser;
class Rewriter {
public:
......@@ -19,6 +22,11 @@ class Rewriter {
// Assumes code has been parsed and scopes have been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
static bool Rewrite(ParseInfo* info);
// Rewrite a list of statements, using the same rules as a top-level program,
// to ensure identical behaviour of completion result.
static bool Rewrite(Parser* parser, DoExpression* expr,
AstValueFactory* factory);
};
......
......@@ -436,6 +436,11 @@ void AsmTyper::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
}
void AsmTyper::VisitDoExpression(DoExpression* expr) {
FAIL(expr, "do-expression encountered");
}
void AsmTyper::VisitConditional(Conditional* expr) {
RECURSE(VisitWithExpectation(expr->condition(), cache_.kInt32,
"condition expected to be integer"));
......
// Copyright 2015 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: --harmony-do-expressions --harmony-sloppy-let --allow-natives-syntax
// Flags: --harmony-default-parameters --harmony-destructuring
function returnValue(v) { return v; }
function MyError() {}
var global = this;
function TestBasic() {
// Looping and lexical declarations
assertEquals(512, returnValue(do {
let n = 2;
for (let i = 0; i < 4; i++) n <<= 2;
}));
// Strings do the right thing
assertEquals("spooky halloween", returnValue(do {
"happy halloween".replace('happy', 'spooky');
}));
// Do expressions with no completion produce an undefined value
assertEquals(undefined, returnValue(do {}));
assertEquals(undefined, returnValue(do { var x = 99; }));
assertEquals(undefined, returnValue(do { function f() {}; }));
assertEquals(undefined, returnValue(do { let z = 33; }));
// Propagation of exception
assertThrows(function() {
(do {
throw new MyError();
"potatoes";
});
}, MyError);
assertThrows(function() {
return do {
throw new MyError();
"potatoes";
};
}, MyError);
// Return value within do-block overrides `return |do-expression|`
assertEquals("inner-return", (function() {
return "outer-return" + do {
return "inner-return";
"";
};
})());
var count = 0, n = 1;
// Breaking out |do-expression|
assertEquals(3, (function() {
for (var i = 0; i < 10; ++i) (count += 2 * do { if (i === 3) break; ++n });
return i;
})());
// (2 * 2) + (2 * 3) + (2 * 4)
assertEquals(18, count);
// Continue in |do-expression|
count = 0, n = 1;
assertEquals([1, 3, 5, 7, 9], (function() {
var values = [];
for (var i = 0; i < 10; ++i) {
count += 2 * (do {
if ((i & 1) === 0) continue;
values.push(i);
++n;
}) + 1;
}
// (2*2) + 1 + (2*3) + 1 + (2*4) + 1 + (2*5) + 1 + (2*6) + 1
return values;
})());
assertEquals(count, 45);
assertThrows("(do { break; });", SyntaxError);
assertThrows("(do { continue; });", SyntaxError);
// Real-world use case for desugaring
var array = [1, 2, 3, 4, 5], iterable = [6, 7, 8,9];
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], do {
for (var element of iterable) array.push(element);
array;
});
// Nested do-expressions
assertEquals(125, do { (do { (do { 5 * 5 * 5 }) }) });
// Directives are not honoured
(do {
"use strict";
foo = 80;
assertEquals(foo, 80);
});
// Non-empty operand stack testing
var O = {
method1() {
let x = 256;
return x + do {
for (var i = 0; i < 4; ++i) x += i;
} + 17;
},
method2() {
let x = 256;
this.reset();
return x + do {
for (var i = 0; i < this.length(); ++i) x += this.index() * 2;
};
},
_index: 0,
index() {
return ++this._index;
},
_length: 4,
length() { return this._length; },
reset() { this._index = 0; }
};
assertEquals(535, O["method" + do { 1 } + ""]());
assertEquals(532, O["method" + do { ({ valueOf() { return "2"; } }); }]());
assertEquals(532, O[
do { let s = ""; for (let c of "method") s += c; } + "2"]());
}
TestBasic();
function TestDeoptimization1() {
function f(v) {
return 88 + do {
v.a * v.b + v.c;
};
}
var o1 = {};
o1.a = 10;
o1.b = 5;
o1.c = 50;
var o2 = {};
o2.c = 100;
o2.a = 10;
o2.b = 10;
assertEquals(188, f(o1));
assertEquals(188, f(o1));
%OptimizeFunctionOnNextCall(f);
assertEquals(188, f(o1));
assertOptimized(f);
assertEquals(288, f(o2));
assertUnoptimized(f);
assertEquals(288, f(o2));
}
TestDeoptimization1();
function TestInParameterInitializers() {
var first_name = "George";
var last_name = "Jetson";
function fn1(name = do { first_name + " " + last_name }) {
return name;
}
assertEquals("George Jetson", fn1());
var _items = [1, 2, 3, NaN, 4, 5];
function fn2(items = do {
let items = [];
for (var el of _items) {
if (el !== el) break;
items.push(el), items;
}
}) {
return items;
}
assertEquals([1, 2, 3], fn2());
function thrower() { throw new MyError(); }
function fn3(exception = do { try { thrower(); } catch (e) { e } }) {
return exception;
}
assertDoesNotThrow(fn3);
assertInstanceof(fn3(), MyError);
function fn4(exception = do { throw new MyError() }) {}
function catcher(fn) {
try {
fn();
assertUnreachable("fn() initializer should throw");
} catch (e) {
assertInstanceof(e, MyError);
}
}
catcher(fn4);
}
TestInParameterInitializers();
function TestWithEval() {
(function sloppy1() {
assertEquals(do { eval("var x = 5"), x }, 5);
assertEquals(x, 5);
})();
assertThrows(function strict1() {
"use strict";
(do { eval("var x = 5"), x }, 5);
}, ReferenceError);
assertThrows(function strict2() {
(do { eval("'use strict'; var x = 5"), x }, 5);
}, ReferenceError);
}
TestWithEval();
function TestHoisting() {
(do { var a = 1; });
assertEquals(a, 1);
assertEquals(global.a, undefined);
(do {
for (let it of [1, 2, 3, 4, 5]) {
var b = it;
}
});
assertEquals(b, 5);
assertEquals(global.b, undefined);
{
let x = 1
// TODO(caitp): ensure VariableStatements in |do-expressions| in parameter
// initializers, are evaluated in the same VariableEnvironment as they would
// be for eval().
// function f1(a = do { var x = 2 }, b = x) { return b }
// assertEquals(1, f1())
// function f2(a = x, b = do { var x = 2 }) { return a }
// assertEquals(1, f2())
function f3({a = do { var x = 2 }, b = x}) { return b }
assertEquals(2, f3({}))
function f4({a = x, b = do { var x = 2 }}) { return b }
assertEquals(undefined, f4({}))
function f5(a = do { var y = 0 }) {}
assertThrows(() => y, ReferenceError)
}
// TODO(caitp): Always block-scope function declarations in |do| expressions
//(do {
// assertEquals(true, inner_func());
// function inner_func() { return true; }
//});
//assertThrows(function() { return innerFunc(); }, ReferenceError);
}
TestHoisting();
function TestOSR() {
var numbers = do {
let nums = [];
for (let i = 0; i < 1000; ++i) {
let value = (Math.random() * 100) | 0;
nums.push(value === 0 ? 1 : value), nums;
}
};
assertEquals(numbers.length, 1000);
}
for (var i = 0; i < 64; ++i) TestOSR();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment