This commit is contained in:
Jarred Sumner
2021-04-29 10:29:25 -07:00
parent b37acf309c
commit 4e3f680ac4

View File

@@ -264,7 +264,11 @@ const ScopeOrder = struct {
loc: logger.Loc,
scope: *js_ast.Scope,
};
const EnumValueType = enum {
unknown,
string,
numeric,
};
pub const Result = js_ast.Result;
const ParenExprOpts = struct {
@@ -6468,7 +6472,7 @@ pub const P = struct {
data.value = p.visitExpr(val);
// "return undefined;" can safely just always be "return;"
if (@as(Expr.Tag, data.value.?.data) == .e_undefined) {
if (data.value != null and @as(Expr.Tag, data.value.?.data) == .e_undefined) {
// Returning undefined is implicit
data.value = null;
}
@@ -6585,22 +6589,259 @@ pub const P = struct {
// p.lowerObjectRestInForLoopInit(s.Init, &s.Body)
},
.s_try => |data| {
notimpl();
{
p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable;
defer p.popScope();
p.fn_or_arrow_data_visit.try_body_count += 1;
defer p.fn_or_arrow_data_visit.try_body_count -= 1;
var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body);
p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
data.body = _stmts.toOwnedSlice();
}
if (data.catch_) |*catch_| {
p.pushScopeForVisitPass(.block, catch_.loc) catch unreachable;
defer p.popScope();
var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body);
p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
catch_.body = _stmts.toOwnedSlice();
}
if (data.finally) |*finally| {
p.pushScopeForVisitPass(.block, finally.loc) catch unreachable;
var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body);
p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
finally.stmts = _stmts.toOwnedSlice();
p.popScope();
}
},
.s_switch => |data| {
notimpl();
data.test_ = p.visitExpr(data.test_);
{
p.pushScopeForVisitPass(.block, data.body_loc) catch unreachable;
defer p.popScope();
var old_is_inside_Swsitch = p.fn_or_arrow_data_visit.is_inside_switch;
p.fn_or_arrow_data_visit.is_inside_switch = true;
defer p.fn_or_arrow_data_visit.is_inside_switch = old_is_inside_Swsitch;
var i: usize = 0;
while (i < data.cases.len) : (i += 1) {
const case = data.cases[i];
if (case.value) |val| {
data.cases[i].value = p.visitExpr(val);
// TODO: error messages
// Check("case", *c.Value, c.Value.Loc)
// p.warnAboutTypeofAndString(s.Test, *c.Value)
}
var _stmts = List(Stmt).fromOwnedSlice(p.allocator, case.body);
p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
data.cases[i].body = _stmts.toOwnedSlice();
}
}
// TODO: duplicate case checker
},
.s_function => |data| {
notimpl();
p.visitFunc(&data.func, data.func.open_parens_loc);
// Handle exporting this function from a namespace
if (data.func.flags.is_export and p.enclosing_namespace_arg_ref != null) {
const enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref orelse unreachable;
stmts.ensureUnusedCapacity(3) catch unreachable;
stmts.appendAssumeCapacity(stmt.*);
// i wonder if this will crash
stmts.appendAssumeCapacity(Expr.assignStmt(p.e(E.Dot{
.target = p.e(E.Identifier{ .ref = enclosing_namespace_arg_ref }, stmt.loc),
.name = p.symbols.items[data.func.name.?.ref.?.inner_index].original_name,
.name_loc = data.func.name.?.loc,
}, stmt.loc), p.e(E.Identifier{ .ref = data.func.name.?.ref.? }, data.func.name.?.loc), p.allocator));
} else {
stmts.ensureUnusedCapacity(2) catch unreachable;
stmts.appendAssumeCapacity(stmt.*);
}
stmts.appendAssumeCapacity(
// i wonder if this will crash
p.keepStmtSymbolName(
data.func.name.?.loc,
data.func.name.?.ref.?,
p.symbols.items[data.func.name.?.ref.?.inner_index].original_name,
),
);
return;
},
.s_class => |data| {
notimpl();
const shadow_ref = p.visitClass(stmt.loc, &data.class);
// Remove the export flag inside a namespace
const was_export_inside_namespace = data.is_export and p.enclosing_namespace_arg_ref != null;
if (was_export_inside_namespace) {
data.is_export = false;
}
// Lower class field syntax for browsers that don't support it
stmts.appendSlice(p.lowerClass(js_ast.StmtOrExpr{ .stmt = stmt.* }, shadow_ref)) catch unreachable;
// Handle exporting this class from a namespace
if (was_export_inside_namespace) {
stmts.appendAssumeCapacity(Expr.assignStmt(p.e(E.Dot{
.target = p.e(E.Identifier{ .ref = p.enclosing_namespace_arg_ref.? }, stmt.loc),
.name = p.symbols.items[data.class.class_name.?.ref.?.inner_index].original_name,
.name_loc = data.class.class_name.?.loc,
}, stmt.loc), p.e(E.Identifier{ .ref = data.class.class_name.?.ref.? }, data.class.class_name.?.loc), p.allocator));
}
return;
},
.s_enum => |data| {
notimpl();
p.recordDeclaredSymbol(data.name.ref.?) catch unreachable;
p.pushScopeForVisitPass(.entry, stmt.loc) catch unreachable;
defer p.popScope();
p.recordDeclaredSymbol(data.arg) catch unreachable;
// Scan ahead for any variables inside this namespace. This must be done
// ahead of time before visiting any statements inside the namespace
// because we may end up visiting the uses before the declarations.
// We need to convert the uses into property accesses on the namespace.
for (data.values) |value| {
if (!value.ref.isNull()) {
p.is_exported_inside_namespace.put(value.ref, data.arg) catch unreachable;
}
}
// Values without initializers are initialized to one more than the
// previous value if the previous value is numeric. Otherwise values
// without initializers are initialized to undefined.
var next_numeric_value: f64 = 0.0;
var has_numeric_value = true;
var value_exprs = List(Expr).initCapacity(p.allocator, data.values.len) catch unreachable;
// Track values so they can be used by constant folding. We need to follow
// links here in case the enum was merged with a preceding namespace
var values_so_far = std.StringHashMap(f64).init(p.allocator);
p.known_enum_values.put(data.name.ref orelse p.panic("Expected data.name.ref", .{}), values_so_far) catch unreachable;
p.known_enum_values.put(data.arg, values_so_far) catch unreachable;
// We normally don't fold numeric constants because they might increase code
// size, but it's important to fold numeric constants inside enums since
// that's what the TypeScript compiler does.
const old_should_fold_numeric_constants = p.should_fold_numeric_constants;
p.should_fold_numeric_constants = true;
defer p.should_fold_numeric_constants = old_should_fold_numeric_constants;
for (data.values) |*enum_value| {
// gotta allocate here so it lives after this function stack frame goes poof
const name = p.lexer.utf16ToString(enum_value.name);
var assign_target: Expr = undefined;
var enum_value_type: EnumValueType = EnumValueType.unknown;
if (enum_value.value != null) {
enum_value.value = p.visitExpr(enum_value.value.?);
switch (enum_value.value.?.data) {
.e_number => |num| {
values_so_far.put(name, num.value) catch unreachable;
enum_value_type = .numeric;
next_numeric_value = num.value + 1.0;
},
.e_string => |str| {
enum_value_type = .string;
},
else => {},
}
} else if (enum_value_type == .numeric) {
enum_value.value = p.e(E.Number{ .value = next_numeric_value }, enum_value.loc);
values_so_far.put(name, next_numeric_value) catch unreachable;
next_numeric_value += 1;
} else {
enum_value.value = p.e(E.Undefined{}, enum_value.loc);
}
// "Enum['Name'] = value"
assign_target = Expr.assign(p.e(E.Index{
.target = p.e(
E.Identifier{ .ref = data.arg },
enum_value.loc,
),
.index = p.e(
E.String{ .value = enum_value.name },
enum_value.loc,
),
}, enum_value.loc), enum_value.value orelse unreachable, p.allocator);
p.recordUsage(&data.arg);
// String-valued enums do not form a two-way map
if (enum_value_type == .string) {
value_exprs.append(assign_target) catch unreachable;
} else {
// "Enum[assignTarget] = 'Name'"
value_exprs.append(Expr.assign(p.e(E.Index{
.target = p.e(
E.Identifier{ .ref = data.arg },
enum_value.loc,
),
.index = assign_target,
}, enum_value.loc), p.e(E.String{ .value = enum_value.name }, enum_value.loc), p.allocator)) catch unreachable;
}
}
p.recordUsage(&data.arg);
var value_stmts = List(Stmt).initCapacity(p.allocator, value_exprs.items.len) catch unreachable;
// Generate statements from expressions
for (value_exprs.items) |expr| {
value_stmts.appendAssumeCapacity(p.s(S.SExpr{ .value = expr }, expr.loc));
}
value_exprs.deinit();
p.generateClosureForTypescriptNameSpaceOrEnum(
stmts,
stmt.loc,
data.is_export,
data.name.loc,
data.name.ref.?,
data.arg,
value_stmts.toOwnedSlice(),
);
return;
},
.s_namespace => |data| {
notimpl();
p.recordDeclaredSymbol(data.name.ref.?) catch unreachable;
// Scan ahead for any variables inside this namespace. This must be done
// ahead of time before visiting any statements inside the namespace
// because we may end up visiting the uses before the declarations.
// We need to convert the uses into property accesses on the namespace.
for (data.stmts) |child_stmt| {
switch (child_stmt.data) {
.s_local => |local| {
if (local.is_export) {
p.markExportedDeclsInsideNamespace(data.arg, local.decls);
}
},
else => {},
}
}
var prepend_temp_refs = PrependTempRefsOpts{ .kind = StmtsKind.fn_body };
var prepend_list = List(Stmt).fromOwnedSlice(p.allocator, data.stmts);
{
const old_enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref;
p.enclosing_namespace_arg_ref = data.arg;
defer p.enclosing_namespace_arg_ref = old_enclosing_namespace_arg_ref;
p.pushScopeForVisitPass(.entry, stmt.loc) catch unreachable;
defer p.popScope();
p.recordDeclaredSymbol(data.arg) catch unreachable;
p.visitStmtsAndPrependTempRefs(&prepend_list, &prepend_temp_refs) catch unreachable;
}
p.generateClosureForTypescriptNameSpaceOrEnum(
stmts,
stmt.loc,
data.is_export,
data.name.loc,
data.name.ref.?,
data.arg,
prepend_list.toOwnedSlice(),
);
return;
},
else => {
notimpl();
@@ -6611,6 +6852,27 @@ pub const P = struct {
try stmts.append(stmt.*);
}
pub fn markExportedDeclsInsideNamespace(p: *P, ns_ref: Ref, decls: []G.Decl) void {
notimpl();
}
pub fn generateClosureForTypescriptNameSpaceOrEnum(
p: *P,
stmts: *List(Stmt),
stmt_loc: logger.Loc,
is_export: bool,
name_loc: logger.Loc,
name_ref: Ref,
arg_ref: Ref,
stmts_inside_closure: []Stmt,
) void {
notimpl();
}
pub fn lowerClass(p: *P, stmtorexpr: js_ast.StmtOrExpr, ref: Ref) []Stmt {
notimpl();
}
pub fn visitForLoopInit(p: *P, stmt: Stmt, is_in_or_of: bool) Stmt {
switch (stmt.data) {
.s_expr => |st| {
@@ -6667,8 +6929,81 @@ pub const P = struct {
notimpl();
}
pub fn visitBinding(p: *P, binding: BindingNodeIndex, duplicate_arg_check: ?StringBoolMap) void {
notimpl();
pub fn visitBinding(p: *P, binding: BindingNodeIndex, duplicate_arg_check: ?*StringBoolMap) void {
switch (binding.data) {
.b_missing => {},
.b_identifier => |bind| {
p.recordDeclaredSymbol(bind.ref) catch unreachable;
const name = p.symbols.items[bind.ref.inner_index].original_name;
if (isEvalOrArguments(name)) {
p.markStrictModeFeature(.eval_or_arguments, js_lexer.rangeOfIdentifier(&p.source, binding.loc), name) catch unreachable;
}
if (duplicate_arg_check) |dup| {
const res = dup.getOrPut(name) catch unreachable;
if (res.found_existing) {
p.log.addRangeErrorFmt(
p.source,
js_lexer.rangeOfIdentifier(&p.source, binding.loc),
p.allocator,
"\"{s}\" cannot be bound multiple times in the same parameter list",
.{name},
) catch unreachable;
}
res.entry.value = true;
}
},
.b_array => |bind| {
for (bind.items) |*item| {
p.visitBinding(item.binding, duplicate_arg_check);
if (item.default_value) |default_value| {
const was_anonymous_named_expr = p.isAnonymousNamedExpr(default_value);
item.default_value = p.visitExpr(default_value);
switch (item.binding.data) {
.b_identifier => |bind_| {
item.default_value = p.maybeKeepExprSymbolName(
item.default_value orelse unreachable,
p.symbols.items[bind_.ref.inner_index].original_name,
was_anonymous_named_expr,
);
},
else => {},
}
}
}
},
.b_object => |bind| {
var i: usize = 0;
while (i < bind.properties.len) : (i += 1) {
var property = bind.properties[i];
if (!property.flags.is_spread) {
property.key = p.visitExpr(property.key);
}
p.visitBinding(property.value, duplicate_arg_check);
if (property.default_value) |default_value| {
const was_anonymous_named_expr = p.isAnonymousNamedExpr(default_value);
property.default_value = p.visitExpr(default_value);
switch (property.value.data) {
.b_identifier => |bind_| {
property.default_value = p.maybeKeepExprSymbolName(
property.default_value orelse unreachable,
p.symbols.items[bind_.ref.inner_index].original_name,
was_anonymous_named_expr,
);
},
else => {},
}
}
bind.properties[i] = property;
}
},
else => {
p.panic("Unexpected binding {s}", .{binding});
},
}
}
pub fn visitLoopBody(p: *P, stmt: StmtNodeIndex) StmtNodeIndex {