aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-04-29 10:29:25 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-04-29 10:29:25 -0700
commit1e9e42618b1923fdb65232129a1556c1b962a1a7 (patch)
treeadabbe0f597a4b1bc9bf33d10f105e11bdfccdf5
parentcabe773a4f0a12e411f9f3c9698da6bbd90ec474 (diff)
downloadbun-1e9e42618b1923fdb65232129a1556c1b962a1a7.tar.gz
bun-1e9e42618b1923fdb65232129a1556c1b962a1a7.tar.zst
bun-1e9e42618b1923fdb65232129a1556c1b962a1a7.zip
asdasd
Former-commit-id: 4e3f680ac479552abd24f4d60d98a43d1ecb64c3
-rw-r--r--src/js_parser.zig355
1 files changed, 345 insertions, 10 deletions
diff --git a/src/js_parser.zig b/src/js_parser.zig
index b0339e1aa..5d3653eb5 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -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 {