compiler, runtime: provide index information on bounds check failure
This implements https://golang.org/cl/161477 in the gofrontend. Updates golang/go#30116 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/191881 From-SVN: r274998
This commit is contained in:
parent
464969eb9b
commit
fc4f90f0c8
@ -1,4 +1,4 @@
|
||||
a6ddd0e1208a7d229c10be630c1110b3914038f5
|
||||
189ff44b2c26f29f41f0eb159e0d8f3fa508ecae
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -594,67 +594,110 @@ Expression::backend_numeric_constant_expression(Translate_context* context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Return an expression which evaluates to true if VAL, of arbitrary integer
|
||||
// type, is negative or is more than the maximum value of the Go type "int".
|
||||
// Insert bounds checks for an index expression. Check that that VAL
|
||||
// >= 0 and that it fits in an int. Then check that VAL OP BOUND is
|
||||
// true. If any condition is false, call one of the CODE runtime
|
||||
// functions, which will panic.
|
||||
|
||||
Expression*
|
||||
Expression::check_bounds(Expression* val, Location loc)
|
||||
void
|
||||
Expression::check_bounds(Expression* val, Operator op, Expression* bound,
|
||||
Runtime::Function code,
|
||||
Runtime::Function code_u,
|
||||
Runtime::Function code_extend,
|
||||
Runtime::Function code_extend_u,
|
||||
Statement_inserter* inserter,
|
||||
Location loc)
|
||||
{
|
||||
go_assert(val->is_variable() || val->is_constant());
|
||||
go_assert(bound->is_variable() || bound->is_constant());
|
||||
|
||||
Type* int_type = Type::lookup_integer_type("int");
|
||||
int int_type_size = int_type->integer_type()->bits();
|
||||
|
||||
Type* val_type = val->type();
|
||||
Type* bound_type = Type::lookup_integer_type("int");
|
||||
|
||||
int val_type_size;
|
||||
bool val_is_unsigned = false;
|
||||
if (val_type->integer_type() != NULL)
|
||||
if (val_type->integer_type() == NULL)
|
||||
{
|
||||
val_type_size = val_type->integer_type()->bits();
|
||||
val_is_unsigned = val_type->integer_type()->is_unsigned();
|
||||
go_assert(saw_errors());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!val_type->is_numeric_type()
|
||||
|| !Type::are_convertible(bound_type, val_type, NULL))
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
return Expression::make_boolean(true, loc);
|
||||
}
|
||||
int val_type_size = val_type->integer_type()->bits();
|
||||
bool val_is_unsigned = val_type->integer_type()->is_unsigned();
|
||||
|
||||
if (val_type->complex_type() != NULL)
|
||||
val_type_size = val_type->complex_type()->bits();
|
||||
else
|
||||
val_type_size = val_type->float_type()->bits();
|
||||
}
|
||||
|
||||
Expression* negative_index = Expression::make_boolean(false, loc);
|
||||
Expression* index_overflows = Expression::make_boolean(false, loc);
|
||||
// Check that VAL >= 0.
|
||||
Expression* check = NULL;
|
||||
if (!val_is_unsigned)
|
||||
{
|
||||
Expression* zero = Expression::make_integer_ul(0, val_type, loc);
|
||||
negative_index = Expression::make_binary(OPERATOR_LT, val, zero, loc);
|
||||
check = Expression::make_binary(OPERATOR_GE, val->copy(), zero, loc);
|
||||
}
|
||||
|
||||
int bound_type_size = bound_type->integer_type()->bits();
|
||||
if (val_type_size > bound_type_size
|
||||
|| (val_type_size == bound_type_size
|
||||
// If VAL's type is larger than int, check that VAL fits in an int.
|
||||
if (val_type_size > int_type_size
|
||||
|| (val_type_size == int_type_size
|
||||
&& val_is_unsigned))
|
||||
{
|
||||
mpz_t one;
|
||||
mpz_init_set_ui(one, 1UL);
|
||||
|
||||
// maxval = 2^(bound_type_size - 1) - 1
|
||||
// maxval = 2^(int_type_size - 1) - 1
|
||||
mpz_t maxval;
|
||||
mpz_init(maxval);
|
||||
mpz_mul_2exp(maxval, one, bound_type_size - 1);
|
||||
mpz_mul_2exp(maxval, one, int_type_size - 1);
|
||||
mpz_sub_ui(maxval, maxval, 1);
|
||||
Expression* max = Expression::make_integer_z(&maxval, val_type, loc);
|
||||
mpz_clear(one);
|
||||
mpz_clear(maxval);
|
||||
|
||||
index_overflows = Expression::make_binary(OPERATOR_GT, val, max, loc);
|
||||
Expression* cmp = Expression::make_binary(OPERATOR_LE, val->copy(),
|
||||
max, loc);
|
||||
if (check == NULL)
|
||||
check = cmp;
|
||||
else
|
||||
check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc);
|
||||
}
|
||||
|
||||
return Expression::make_binary(OPERATOR_OROR, negative_index, index_overflows,
|
||||
loc);
|
||||
// For the final check we can assume that VAL fits in an int.
|
||||
Expression* ival;
|
||||
if (val_type == int_type)
|
||||
ival = val->copy();
|
||||
else
|
||||
ival = Expression::make_cast(int_type, val->copy(), loc);
|
||||
|
||||
// BOUND is assumed to fit in an int. Either it comes from len or
|
||||
// cap, or it was checked by an earlier call.
|
||||
Expression* ibound;
|
||||
if (bound->type() == int_type)
|
||||
ibound = bound->copy();
|
||||
else
|
||||
ibound = Expression::make_cast(int_type, bound->copy(), loc);
|
||||
|
||||
Expression* cmp = Expression::make_binary(op, ival, ibound, loc);
|
||||
if (check == NULL)
|
||||
check = cmp;
|
||||
else
|
||||
check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc);
|
||||
|
||||
Runtime::Function c;
|
||||
if (val_type_size > int_type_size)
|
||||
{
|
||||
if (val_is_unsigned)
|
||||
c = code_extend_u;
|
||||
else
|
||||
c = code_extend;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (val_is_unsigned)
|
||||
c = code_u;
|
||||
else
|
||||
c = code;
|
||||
}
|
||||
|
||||
Expression* ignore = Expression::make_boolean(true, loc);
|
||||
Expression* crash = Runtime::make_call(c, loc, 2,
|
||||
val->copy(), bound->copy());
|
||||
Expression* cond = Expression::make_conditional(check, ignore, crash, loc);
|
||||
inserter->insert(Statement::make_statement(cond, true));
|
||||
}
|
||||
|
||||
void
|
||||
@ -12666,7 +12709,8 @@ Array_index_expression::do_check_types(Gogo*)
|
||||
unsigned long v;
|
||||
if (this->start_->type()->integer_type() == NULL
|
||||
&& !this->start_->type()->is_error()
|
||||
&& (!this->start_->numeric_constant_value(&nc)
|
||||
&& (!this->start_->type()->is_abstract()
|
||||
|| !this->start_->numeric_constant_value(&nc)
|
||||
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
|
||||
this->report_error(_("index must be integer"));
|
||||
if (this->end_ != NULL
|
||||
@ -12674,7 +12718,8 @@ Array_index_expression::do_check_types(Gogo*)
|
||||
&& !this->end_->type()->is_error()
|
||||
&& !this->end_->is_nil_expression()
|
||||
&& !this->end_->is_error_expression()
|
||||
&& (!this->end_->numeric_constant_value(&nc)
|
||||
&& (!this->end_->type()->is_abstract()
|
||||
|| !this->end_->numeric_constant_value(&nc)
|
||||
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
|
||||
this->report_error(_("slice end must be integer"));
|
||||
if (this->cap_ != NULL
|
||||
@ -12682,7 +12727,8 @@ Array_index_expression::do_check_types(Gogo*)
|
||||
&& !this->cap_->type()->is_error()
|
||||
&& !this->cap_->is_nil_expression()
|
||||
&& !this->cap_->is_error_expression()
|
||||
&& (!this->cap_->numeric_constant_value(&nc)
|
||||
&& (!this->cap_->type()->is_abstract()
|
||||
|| !this->cap_->numeric_constant_value(&nc)
|
||||
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
|
||||
this->report_error(_("slice capacity must be integer"));
|
||||
|
||||
@ -12799,13 +12845,21 @@ Array_index_expression::do_must_eval_subexpressions_in_order(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Flatten array indexing by using temporary variables for slices and indexes.
|
||||
// Flatten array indexing: add temporary variables and bounds checks.
|
||||
|
||||
Expression*
|
||||
Array_index_expression::do_flatten(Gogo*, Named_object*,
|
||||
Array_index_expression::do_flatten(Gogo* gogo, Named_object*,
|
||||
Statement_inserter* inserter)
|
||||
{
|
||||
if (this->is_flattened_)
|
||||
return this;
|
||||
this->is_flattened_ = true;
|
||||
|
||||
Location loc = this->location();
|
||||
|
||||
if (this->is_error_expression())
|
||||
return Expression::make_error(loc);
|
||||
|
||||
Expression* array = this->array_;
|
||||
Expression* start = this->start_;
|
||||
Expression* end = this->end_;
|
||||
@ -12823,34 +12877,157 @@ Array_index_expression::do_flatten(Gogo*, Named_object*,
|
||||
return Expression::make_error(loc);
|
||||
}
|
||||
|
||||
Array_type* array_type = this->array_->type()->array_type();
|
||||
if (array_type == NULL)
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
return Expression::make_error(loc);
|
||||
}
|
||||
|
||||
Temporary_statement* temp;
|
||||
if (array->type()->is_slice_type() && !array->is_variable())
|
||||
if (array_type->is_slice_type() && !array->is_variable())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, array, loc);
|
||||
inserter->insert(temp);
|
||||
this->array_ = Expression::make_temporary_reference(temp, loc);
|
||||
array = this->array_;
|
||||
}
|
||||
if (!start->is_variable())
|
||||
if (!start->is_variable() && !start->is_constant())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, start, loc);
|
||||
inserter->insert(temp);
|
||||
this->start_ = Expression::make_temporary_reference(temp, loc);
|
||||
start = this->start_;
|
||||
}
|
||||
if (end != NULL
|
||||
&& !end->is_nil_expression()
|
||||
&& !end->is_variable())
|
||||
&& !end->is_variable()
|
||||
&& !end->is_constant())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, end, loc);
|
||||
inserter->insert(temp);
|
||||
this->end_ = Expression::make_temporary_reference(temp, loc);
|
||||
end = this->end_;
|
||||
}
|
||||
if (cap != NULL && !cap->is_variable())
|
||||
if (cap != NULL && !cap->is_variable() && !cap->is_constant())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, cap, loc);
|
||||
inserter->insert(temp);
|
||||
this->cap_ = Expression::make_temporary_reference(temp, loc);
|
||||
cap = this->cap_;
|
||||
}
|
||||
|
||||
if (!this->needs_bounds_check_)
|
||||
return this;
|
||||
|
||||
Expression* len;
|
||||
if (!array_type->is_slice_type())
|
||||
{
|
||||
len = array_type->get_length(gogo, this->array_);
|
||||
go_assert(len->is_constant());
|
||||
}
|
||||
else
|
||||
{
|
||||
len = array_type->get_length(gogo, this->array_->copy());
|
||||
temp = Statement::make_temporary(NULL, len, loc);
|
||||
inserter->insert(temp);
|
||||
len = Expression::make_temporary_reference(temp, loc);
|
||||
}
|
||||
|
||||
Expression* scap = NULL;
|
||||
if (array_type->is_slice_type())
|
||||
{
|
||||
scap = array_type->get_capacity(gogo, this->array_->copy());
|
||||
temp = Statement::make_temporary(NULL, scap, loc);
|
||||
inserter->insert(temp);
|
||||
scap = Expression::make_temporary_reference(temp, loc);
|
||||
}
|
||||
|
||||
// The order of bounds checks here matches the order used by the gc
|
||||
// compiler, as tested by issue30116[u].go.
|
||||
|
||||
if (cap != NULL)
|
||||
{
|
||||
if (array_type->is_slice_type())
|
||||
Expression::check_bounds(cap, OPERATOR_LE, scap,
|
||||
Runtime::PANIC_SLICE3_ACAP,
|
||||
Runtime::PANIC_SLICE3_ACAP_U,
|
||||
Runtime::PANIC_EXTEND_SLICE3_ACAP,
|
||||
Runtime::PANIC_EXTEND_SLICE3_ACAP_U,
|
||||
inserter, loc);
|
||||
else
|
||||
Expression::check_bounds(cap, OPERATOR_LE, len,
|
||||
Runtime::PANIC_SLICE3_ALEN,
|
||||
Runtime::PANIC_SLICE3_ALEN_U,
|
||||
Runtime::PANIC_EXTEND_SLICE3_ALEN,
|
||||
Runtime::PANIC_EXTEND_SLICE3_ALEN_U,
|
||||
inserter, loc);
|
||||
|
||||
Expression* start_bound = cap;
|
||||
if (end != NULL && !end->is_nil_expression())
|
||||
{
|
||||
Expression::check_bounds(end, OPERATOR_LE, cap,
|
||||
Runtime::PANIC_SLICE3_B,
|
||||
Runtime::PANIC_SLICE3_B_U,
|
||||
Runtime::PANIC_EXTEND_SLICE3_B,
|
||||
Runtime::PANIC_EXTEND_SLICE3_B_U,
|
||||
inserter, loc);
|
||||
start_bound = end;
|
||||
}
|
||||
|
||||
Expression::check_bounds(start, OPERATOR_LE, start_bound,
|
||||
Runtime::PANIC_SLICE3_C,
|
||||
Runtime::PANIC_SLICE3_C_U,
|
||||
Runtime::PANIC_EXTEND_SLICE3_C,
|
||||
Runtime::PANIC_EXTEND_SLICE3_C_U,
|
||||
inserter, loc);
|
||||
}
|
||||
else if (end != NULL && !end->is_nil_expression())
|
||||
{
|
||||
if (array_type->is_slice_type())
|
||||
Expression::check_bounds(end, OPERATOR_LE, scap,
|
||||
Runtime::PANIC_SLICE_ACAP,
|
||||
Runtime::PANIC_SLICE_ACAP_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_ACAP,
|
||||
Runtime::PANIC_EXTEND_SLICE_ACAP_U,
|
||||
inserter, loc);
|
||||
else
|
||||
Expression::check_bounds(end, OPERATOR_LE, len,
|
||||
Runtime::PANIC_SLICE_ALEN,
|
||||
Runtime::PANIC_SLICE_ALEN_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_ALEN,
|
||||
Runtime::PANIC_EXTEND_SLICE_ALEN_U,
|
||||
inserter, loc);
|
||||
|
||||
Expression::check_bounds(start, OPERATOR_LE, end,
|
||||
Runtime::PANIC_SLICE_B,
|
||||
Runtime::PANIC_SLICE_B_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_B,
|
||||
Runtime::PANIC_EXTEND_SLICE_B_U,
|
||||
inserter, loc);
|
||||
}
|
||||
else if (end != NULL)
|
||||
{
|
||||
Expression* start_bound;
|
||||
if (array_type->is_slice_type())
|
||||
start_bound = scap;
|
||||
else
|
||||
start_bound = len;
|
||||
Expression::check_bounds(start, OPERATOR_LE, start_bound,
|
||||
Runtime::PANIC_SLICE_B,
|
||||
Runtime::PANIC_SLICE_B_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_B,
|
||||
Runtime::PANIC_EXTEND_SLICE_B_U,
|
||||
inserter, loc);
|
||||
}
|
||||
else
|
||||
Expression::check_bounds(start, OPERATOR_LT, len,
|
||||
Runtime::PANIC_INDEX,
|
||||
Runtime::PANIC_INDEX_U,
|
||||
Runtime::PANIC_EXTEND_INDEX,
|
||||
Runtime::PANIC_EXTEND_INDEX_U,
|
||||
inserter, loc);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -12899,10 +13076,8 @@ Array_index_expression::do_get_backend(Translate_context* context)
|
||||
Type* int_type = Type::lookup_integer_type("int");
|
||||
Btype* int_btype = int_type->get_backend(gogo);
|
||||
|
||||
// We need to convert the length and capacity to the Go "int" type here
|
||||
// because the length of a fixed-length array could be of type "uintptr"
|
||||
// and gimple disallows binary operations between "uintptr" and other
|
||||
// integer types. FIXME.
|
||||
// Convert the length and capacity to "int". FIXME: Do we need to
|
||||
// do this?
|
||||
Bexpression* length = NULL;
|
||||
if (this->end_ == NULL || this->end_->is_nil_expression())
|
||||
{
|
||||
@ -12939,53 +13114,18 @@ Array_index_expression::do_get_backend(Translate_context* context)
|
||||
Bexpression* start = this->start_->get_backend(context);
|
||||
start = gogo->backend()->convert_expression(int_btype, start, loc);
|
||||
|
||||
Bexpression* crash = NULL;
|
||||
Bexpression* bad_index = NULL;
|
||||
if (this->needs_bounds_check_)
|
||||
{
|
||||
int code = (array_type->length() != NULL
|
||||
? (this->end_ == NULL
|
||||
? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS
|
||||
: RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS)
|
||||
: (this->end_ == NULL
|
||||
? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS
|
||||
: RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS));
|
||||
crash = gogo->runtime_error(code, loc)->get_backend(context);
|
||||
bad_index = Expression::check_bounds(this->start_, loc)->get_backend(context);
|
||||
Bexpression* start_too_large =
|
||||
gogo->backend()->binary_expression((this->end_ == NULL
|
||||
? OPERATOR_GE
|
||||
: OPERATOR_GT),
|
||||
start,
|
||||
(this->end_ == NULL
|
||||
? length
|
||||
: capacity),
|
||||
loc);
|
||||
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR,
|
||||
start_too_large,
|
||||
bad_index, loc);
|
||||
}
|
||||
|
||||
|
||||
Bfunction* bfn = context->function()->func_value()->get_decl();
|
||||
if (this->end_ == NULL)
|
||||
{
|
||||
// Simple array indexing. This has to return an l-value, so
|
||||
// wrap the index check into START.
|
||||
if (this->needs_bounds_check_)
|
||||
start =
|
||||
gogo->backend()->conditional_expression(bfn, int_btype, bad_index,
|
||||
crash, start, loc);
|
||||
|
||||
// Simple array indexing.
|
||||
Bexpression* ret;
|
||||
if (array_type->length() != NULL)
|
||||
if (!array_type->is_slice_type())
|
||||
{
|
||||
Bexpression* array = this->array_->get_backend(context);
|
||||
ret = gogo->backend()->array_index_expression(array, start, loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Slice.
|
||||
Expression* valptr =
|
||||
array_type->get_value_pointer(gogo, this->array_,
|
||||
this->is_lvalue_);
|
||||
@ -12999,31 +13139,7 @@ Array_index_expression::do_get_backend(Translate_context* context)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Array slice.
|
||||
|
||||
if (this->cap_ != NULL)
|
||||
{
|
||||
cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc);
|
||||
|
||||
if (this->needs_bounds_check_)
|
||||
{
|
||||
Bexpression* bounds_bcheck =
|
||||
Expression::check_bounds(this->cap_, loc)->get_backend(context);
|
||||
bad_index =
|
||||
gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck,
|
||||
bad_index, loc);
|
||||
|
||||
Bexpression* cap_too_small =
|
||||
gogo->backend()->binary_expression(OPERATOR_LT, cap_arg, start, loc);
|
||||
Bexpression* cap_too_large =
|
||||
gogo->backend()->binary_expression(OPERATOR_GT, cap_arg, capacity, loc);
|
||||
Bexpression* bad_cap =
|
||||
gogo->backend()->binary_expression(OPERATOR_OROR, cap_too_small,
|
||||
cap_too_large, loc);
|
||||
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_cap,
|
||||
bad_index, loc);
|
||||
}
|
||||
}
|
||||
// Slice expression.
|
||||
|
||||
Bexpression* end;
|
||||
if (this->end_->is_nil_expression())
|
||||
@ -13032,24 +13148,6 @@ Array_index_expression::do_get_backend(Translate_context* context)
|
||||
{
|
||||
end = this->end_->get_backend(context);
|
||||
end = gogo->backend()->convert_expression(int_btype, end, loc);
|
||||
if (this->needs_bounds_check_)
|
||||
{
|
||||
Bexpression* bounds_bcheck =
|
||||
Expression::check_bounds(this->end_, loc)->get_backend(context);
|
||||
bad_index =
|
||||
gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck,
|
||||
bad_index, loc);
|
||||
|
||||
Bexpression* end_too_small =
|
||||
gogo->backend()->binary_expression(OPERATOR_LT, end, start, loc);
|
||||
Bexpression* end_too_large =
|
||||
gogo->backend()->binary_expression(OPERATOR_GT, end, cap_arg, loc);
|
||||
Bexpression* bad_end =
|
||||
gogo->backend()->binary_expression(OPERATOR_OROR, end_too_small,
|
||||
end_too_large, loc);
|
||||
bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_end,
|
||||
bad_index, loc);
|
||||
}
|
||||
}
|
||||
|
||||
Bexpression* result_length =
|
||||
@ -13081,12 +13179,7 @@ Array_index_expression::do_get_backend(Translate_context* context)
|
||||
init.push_back(result_length);
|
||||
init.push_back(result_capacity);
|
||||
|
||||
Bexpression* ret =
|
||||
gogo->backend()->constructor_expression(struct_btype, init, loc);
|
||||
if (this->needs_bounds_check_)
|
||||
ret = gogo->backend()->conditional_expression(bfn, struct_btype, bad_index,
|
||||
crash, ret, loc);
|
||||
return ret;
|
||||
return gogo->backend()->constructor_expression(struct_btype, init, loc);
|
||||
}
|
||||
|
||||
// Export an array index expression.
|
||||
@ -13164,7 +13257,15 @@ Expression*
|
||||
String_index_expression::do_flatten(Gogo*, Named_object*,
|
||||
Statement_inserter* inserter)
|
||||
{
|
||||
if (this->is_flattened_)
|
||||
return this;
|
||||
this->is_flattened_ = true;
|
||||
|
||||
Location loc = this->location();
|
||||
|
||||
if (this->is_error_expression())
|
||||
return Expression::make_error(loc);
|
||||
|
||||
Expression* string = this->string_;
|
||||
Expression* start = this->start_;
|
||||
Expression* end = this->end_;
|
||||
@ -13180,27 +13281,69 @@ String_index_expression::do_flatten(Gogo*, Named_object*,
|
||||
}
|
||||
|
||||
Temporary_statement* temp;
|
||||
if (!this->string_->is_variable())
|
||||
if (!string->is_variable())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, this->string_, loc);
|
||||
temp = Statement::make_temporary(NULL, string, loc);
|
||||
inserter->insert(temp);
|
||||
this->string_ = Expression::make_temporary_reference(temp, loc);
|
||||
string = this->string_;
|
||||
}
|
||||
if (!this->start_->is_variable())
|
||||
if (!start->is_variable())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, this->start_, loc);
|
||||
temp = Statement::make_temporary(NULL, start, loc);
|
||||
inserter->insert(temp);
|
||||
this->start_ = Expression::make_temporary_reference(temp, loc);
|
||||
start = this->start_;
|
||||
}
|
||||
if (this->end_ != NULL
|
||||
&& !this->end_->is_nil_expression()
|
||||
&& !this->end_->is_variable())
|
||||
if (end != NULL
|
||||
&& !end->is_nil_expression()
|
||||
&& !end->is_variable())
|
||||
{
|
||||
temp = Statement::make_temporary(NULL, this->end_, loc);
|
||||
temp = Statement::make_temporary(NULL, end, loc);
|
||||
inserter->insert(temp);
|
||||
this->end_ = Expression::make_temporary_reference(temp, loc);
|
||||
end = this->end_;
|
||||
}
|
||||
|
||||
Expression* len = Expression::make_string_info(string->copy(),
|
||||
STRING_INFO_LENGTH, loc);
|
||||
temp = Statement::make_temporary(NULL, len, loc);
|
||||
inserter->insert(temp);
|
||||
len = Expression::make_temporary_reference(temp, loc);
|
||||
|
||||
// The order of bounds checks here matches the order used by the gc
|
||||
// compiler, as tested by issue30116[u].go.
|
||||
|
||||
if (end != NULL && !end->is_nil_expression())
|
||||
{
|
||||
Expression::check_bounds(end, OPERATOR_LE, len,
|
||||
Runtime::PANIC_SLICE_ALEN,
|
||||
Runtime::PANIC_SLICE_ALEN_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_ALEN,
|
||||
Runtime::PANIC_EXTEND_SLICE_ALEN_U,
|
||||
inserter, loc);
|
||||
Expression::check_bounds(start, OPERATOR_LE, end,
|
||||
Runtime::PANIC_SLICE_B,
|
||||
Runtime::PANIC_SLICE_B_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_B,
|
||||
Runtime::PANIC_EXTEND_SLICE_B_U,
|
||||
inserter, loc);
|
||||
}
|
||||
else if (end != NULL)
|
||||
Expression::check_bounds(start, OPERATOR_LE, len,
|
||||
Runtime::PANIC_SLICE_B,
|
||||
Runtime::PANIC_SLICE_B_U,
|
||||
Runtime::PANIC_EXTEND_SLICE_B,
|
||||
Runtime::PANIC_EXTEND_SLICE_B_U,
|
||||
inserter, loc);
|
||||
else
|
||||
Expression::check_bounds(start, OPERATOR_LT, len,
|
||||
Runtime::PANIC_INDEX,
|
||||
Runtime::PANIC_INDEX_U,
|
||||
Runtime::PANIC_EXTEND_INDEX,
|
||||
Runtime::PANIC_EXTEND_INDEX_U,
|
||||
inserter, loc);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -13245,7 +13388,8 @@ String_index_expression::do_check_types(Gogo*)
|
||||
unsigned long v;
|
||||
if (this->start_->type()->integer_type() == NULL
|
||||
&& !this->start_->type()->is_error()
|
||||
&& (!this->start_->numeric_constant_value(&nc)
|
||||
&& (!this->start_->type()->is_abstract()
|
||||
|| !this->start_->numeric_constant_value(&nc)
|
||||
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
|
||||
this->report_error(_("index must be integer"));
|
||||
if (this->end_ != NULL
|
||||
@ -13253,7 +13397,8 @@ String_index_expression::do_check_types(Gogo*)
|
||||
&& !this->end_->type()->is_error()
|
||||
&& !this->end_->is_nil_expression()
|
||||
&& !this->end_->is_error_expression()
|
||||
&& (!this->end_->numeric_constant_value(&nc)
|
||||
&& (!this->end_->type()->is_abstract()
|
||||
|| !this->end_->numeric_constant_value(&nc)
|
||||
|| nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
|
||||
this->report_error(_("slice end must be integer"));
|
||||
|
||||
@ -13303,14 +13448,7 @@ Bexpression*
|
||||
String_index_expression::do_get_backend(Translate_context* context)
|
||||
{
|
||||
Location loc = this->location();
|
||||
Expression* bad_index = Expression::check_bounds(this->start_, loc);
|
||||
|
||||
int code = (this->end_ == NULL
|
||||
? RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS
|
||||
: RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS);
|
||||
|
||||
Gogo* gogo = context->gogo();
|
||||
Bexpression* crash = gogo->runtime_error(code, loc)->get_backend(context);
|
||||
|
||||
Type* int_type = Type::lookup_integer_type("int");
|
||||
|
||||
@ -13342,21 +13480,9 @@ String_index_expression::do_get_backend(Translate_context* context)
|
||||
|
||||
if (this->end_ == NULL)
|
||||
{
|
||||
Expression* start_too_large =
|
||||
Expression::make_binary(OPERATOR_GE, start, length, loc);
|
||||
bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large,
|
||||
bad_index, loc);
|
||||
|
||||
ptr = gogo->backend()->pointer_offset_expression(ptr, bstart, loc);
|
||||
Btype* ubtype = Type::lookup_integer_type("uint8")->get_backend(gogo);
|
||||
Bexpression* index =
|
||||
gogo->backend()->indirect_expression(ubtype, ptr, true, loc);
|
||||
|
||||
Btype* byte_btype = bytes->type()->points_to()->get_backend(gogo);
|
||||
Bexpression* index_error = bad_index->get_backend(context);
|
||||
return gogo->backend()->conditional_expression(bfn, byte_btype,
|
||||
index_error, crash,
|
||||
index, loc);
|
||||
return gogo->backend()->indirect_expression(ubtype, ptr, true, loc);
|
||||
}
|
||||
|
||||
Expression* end = NULL;
|
||||
@ -13365,20 +13491,8 @@ String_index_expression::do_get_backend(Translate_context* context)
|
||||
else
|
||||
{
|
||||
go_assert(this->end_->is_variable());
|
||||
Expression* bounds_check = Expression::check_bounds(this->end_, loc);
|
||||
bad_index =
|
||||
Expression::make_binary(OPERATOR_OROR, bounds_check, bad_index, loc);
|
||||
end = Expression::make_cast(int_type, this->end_, loc);
|
||||
|
||||
Expression* end_too_large =
|
||||
Expression::make_binary(OPERATOR_GT, end, length, loc);
|
||||
bad_index = Expression::make_binary(OPERATOR_OROR, end_too_large,
|
||||
bad_index, loc);
|
||||
}
|
||||
Expression* start_too_large =
|
||||
Expression::make_binary(OPERATOR_GT, start->copy(), end->copy(), loc);
|
||||
bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large,
|
||||
bad_index, loc);
|
||||
|
||||
end = end->copy();
|
||||
Bexpression* bend = end->get_backend(context);
|
||||
@ -13405,12 +13519,7 @@ String_index_expression::do_get_backend(Translate_context* context)
|
||||
std::vector<Bexpression*> init;
|
||||
init.push_back(ptr);
|
||||
init.push_back(new_length);
|
||||
Bexpression* bstrslice =
|
||||
gogo->backend()->constructor_expression(str_btype, init, loc);
|
||||
|
||||
Bexpression* index_error = bad_index->get_backend(context);
|
||||
return gogo->backend()->conditional_expression(bfn, str_btype, index_error,
|
||||
crash, bstrslice, loc);
|
||||
return gogo->backend()->constructor_expression(str_btype, init, loc);
|
||||
}
|
||||
|
||||
// Export a string index expression.
|
||||
|
@ -1059,10 +1059,11 @@ class Expression
|
||||
static Expression*
|
||||
import_expression(Import_expression*, Location);
|
||||
|
||||
// Return an expression which checks that VAL, of arbitrary integer type,
|
||||
// is non-negative and is not more than the maximum integer value.
|
||||
static Expression*
|
||||
check_bounds(Expression* val, Location);
|
||||
// Insert bounds checks for an index expression.
|
||||
static void
|
||||
check_bounds(Expression* val, Operator, Expression* bound, Runtime::Function,
|
||||
Runtime::Function, Runtime::Function, Runtime::Function,
|
||||
Statement_inserter*, Location);
|
||||
|
||||
// Return an expression for constructing a direct interface type from a
|
||||
// pointer.
|
||||
@ -2998,7 +2999,7 @@ class Array_index_expression : public Expression
|
||||
Expression* end, Expression* cap, Location location)
|
||||
: Expression(EXPRESSION_ARRAY_INDEX, location),
|
||||
array_(array), start_(start), end_(end), cap_(cap), type_(NULL),
|
||||
is_lvalue_(false), needs_bounds_check_(true)
|
||||
is_lvalue_(false), needs_bounds_check_(true), is_flattened_(false)
|
||||
{ }
|
||||
|
||||
// Return the array.
|
||||
@ -3121,6 +3122,8 @@ class Array_index_expression : public Expression
|
||||
bool is_lvalue_;
|
||||
// Whether bounds check is needed.
|
||||
bool needs_bounds_check_;
|
||||
// Whether this has already been flattened.
|
||||
bool is_flattened_;
|
||||
};
|
||||
|
||||
// A string index. This is used for both indexing and slicing.
|
||||
@ -3131,7 +3134,7 @@ class String_index_expression : public Expression
|
||||
String_index_expression(Expression* string, Expression* start,
|
||||
Expression* end, Location location)
|
||||
: Expression(EXPRESSION_STRING_INDEX, location),
|
||||
string_(string), start_(start), end_(end)
|
||||
string_(string), start_(start), end_(end), is_flattened_(false)
|
||||
{ }
|
||||
|
||||
// Return the string being indexed.
|
||||
@ -3203,6 +3206,8 @@ class String_index_expression : public Expression
|
||||
// The end index of a slice. This may be NULL for a single index,
|
||||
// or it may be a nil expression for the length of the string.
|
||||
Expression* end_;
|
||||
// Whether this has already been flattened.
|
||||
bool is_flattened_;
|
||||
};
|
||||
|
||||
// An index into a map.
|
||||
|
@ -6300,6 +6300,7 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no)
|
||||
}
|
||||
|
||||
if (this->asm_name_ == "runtime.gopanic"
|
||||
|| this->asm_name_.compare(0, 15, "runtime.goPanic") == 0
|
||||
|| this->asm_name_ == "__go_runtime_error"
|
||||
|| this->asm_name_ == "runtime.panicdottype"
|
||||
|| this->asm_name_ == "runtime.block")
|
||||
|
@ -30,6 +30,8 @@ enum Runtime_function_type
|
||||
RFT_BOOLPTR,
|
||||
// Go type int, C type intgo.
|
||||
RFT_INT,
|
||||
// Go type uint, C type uintgo.
|
||||
RFT_UINT,
|
||||
// Go type uint8, C type uint8_t.
|
||||
RFT_UINT8,
|
||||
// Go type uint16, C type uint16_t.
|
||||
@ -113,6 +115,10 @@ runtime_function_type(Runtime_function_type bft)
|
||||
t = Type::lookup_integer_type("int");
|
||||
break;
|
||||
|
||||
case RFT_UINT:
|
||||
t = Type::lookup_integer_type("uint");
|
||||
break;
|
||||
|
||||
case RFT_UINT8:
|
||||
t = Type::lookup_integer_type("uint8");
|
||||
break;
|
||||
@ -262,6 +268,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
|
||||
case RFT_BOOL:
|
||||
case RFT_BOOLPTR:
|
||||
case RFT_INT:
|
||||
case RFT_UINT:
|
||||
case RFT_UINT8:
|
||||
case RFT_UINT16:
|
||||
case RFT_INT32:
|
||||
|
@ -499,6 +499,75 @@ DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1",
|
||||
P3(POINTER, UINT8, INT32),
|
||||
R1(UINT8))
|
||||
|
||||
// Panics reporting an index or slice out of bounds error.
|
||||
DEF_GO_RUNTIME(PANIC_INDEX, "runtime.goPanicIndex",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_INDEX_U, "runtime.goPanicIndexU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE_ALEN, "runtime.goPanicSliceAlen",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE_ALEN_U, "runtime.goPanicSliceAlenU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE_ACAP, "runtime.goPanicSliceAcap",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE_ACAP_U, "runtime.goPanicSliceAcapU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE_B, "runtime.goPanicSliceB",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE_B_U, "runtime.goPanicSliceBU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_ALEN, "runtime.goPanicSlice3Alen",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_ALEN_U, "runtime.goPanicSlice3AlenU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_ACAP, "runtime.goPanicSlice3Acap",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_ACAP_U, "runtime.goPanicSlice3AcapU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_B, "runtime.goPanicSlice3B",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_B_U, "runtime.goPanicSlice3BU",
|
||||
P2(UINT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_C, "runtime.goPanicSlice3C",
|
||||
P2(INT, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_SLICE3_C_U, "runtime.goPanicSlice3CU",
|
||||
P2(UINT, INT), R0())
|
||||
|
||||
// Panics reporting an index or slice out of bounds error with a
|
||||
// 64-bit index type. These are only used by 32-bit targets.
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_INDEX, "runtime.goPanicExtendIndex",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_INDEX_U, "runtime.goPanicExtendIndexU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ALEN, "runtime.goPanicExtendSliceAlen",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ALEN_U, "runtime.goPanicExtendSliceAlenU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ACAP, "runtime.goPanicExtendSliceAcap",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ACAP_U, "runtime.goPanicExtendSliceAcapU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_B, "runtime.goPanicExtendSliceB",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_B_U, "runtime.goPanicExtendSliceBU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ALEN, "runtime.goPanicExtendSlice3Alen",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ALEN_U, "runtime.goPanicExtendSlice3AlenU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ACAP, "runtime.goPanicExtendSlice3Acap",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ACAP_U, "runtime.goPanicExtendSlice3AcapU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_B, "runtime.goPanicExtendSlice3B",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_B_U, "runtime.goPanicExtendSlice3BU",
|
||||
P2(UINT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C",
|
||||
P2(INT64, INT), R0())
|
||||
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU",
|
||||
P2(UINT64, INT), R0())
|
||||
|
||||
// Remove helper macros.
|
||||
#undef ABFT6
|
||||
#undef ABFT2
|
||||
|
@ -79,6 +79,21 @@ func unquote(s string) string {
|
||||
return string(r[:j])
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
// itoa converts val to a decimal representation. The result is
|
||||
// written somewhere within buf and the location of the result is returned.
|
||||
// buf must be at least 20 bytes.
|
||||
func itoa(buf []byte, val uint64) []byte {
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return buf[i:]
|
||||
}
|
||||
|
||||
// An errorString represents a runtime error described by a single string.
|
||||
type errorString string
|
||||
|
||||
@ -114,6 +129,99 @@ func (e plainError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// An boundsError represents a an indexing or slicing operation gone wrong.
|
||||
type boundsError struct {
|
||||
x int64
|
||||
y int
|
||||
// Values in an index or slice expression can be signed or unsigned.
|
||||
// That means we'd need 65 bits to encode all possible indexes, from -2^63 to 2^64-1.
|
||||
// Instead, we keep track of whether x should be interpreted as signed or unsigned.
|
||||
// y is known to be nonnegative and to fit in an int.
|
||||
signed bool
|
||||
code boundsErrorCode
|
||||
}
|
||||
|
||||
type boundsErrorCode uint8
|
||||
|
||||
const (
|
||||
boundsIndex boundsErrorCode = iota // s[x], 0 <= x < len(s) failed
|
||||
|
||||
boundsSliceAlen // s[?:x], 0 <= x <= len(s) failed
|
||||
boundsSliceAcap // s[?:x], 0 <= x <= cap(s) failed
|
||||
boundsSliceB // s[x:y], 0 <= x <= y failed (but boundsSliceA didn't happen)
|
||||
|
||||
boundsSlice3Alen // s[?:?:x], 0 <= x <= len(s) failed
|
||||
boundsSlice3Acap // s[?:?:x], 0 <= x <= cap(s) failed
|
||||
boundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen)
|
||||
boundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen)
|
||||
|
||||
// Note: in the above, len(s) and cap(s) are stored in y
|
||||
)
|
||||
|
||||
// boundsErrorFmts provide error text for various out-of-bounds panics.
|
||||
// Note: if you change these strings, you should adjust the size of the buffer
|
||||
// in boundsError.Error below as well.
|
||||
var boundsErrorFmts = [...]string{
|
||||
boundsIndex: "index out of range [%x] with length %y",
|
||||
boundsSliceAlen: "slice bounds out of range [:%x] with length %y",
|
||||
boundsSliceAcap: "slice bounds out of range [:%x] with capacity %y",
|
||||
boundsSliceB: "slice bounds out of range [%x:%y]",
|
||||
boundsSlice3Alen: "slice bounds out of range [::%x] with length %y",
|
||||
boundsSlice3Acap: "slice bounds out of range [::%x] with capacity %y",
|
||||
boundsSlice3B: "slice bounds out of range [:%x:%y]",
|
||||
boundsSlice3C: "slice bounds out of range [%x:%y:]",
|
||||
}
|
||||
|
||||
// boundsNegErrorFmts are overriding formats if x is negative. In this case there's no need to report y.
|
||||
var boundsNegErrorFmts = [...]string{
|
||||
boundsIndex: "index out of range [%x]",
|
||||
boundsSliceAlen: "slice bounds out of range [:%x]",
|
||||
boundsSliceAcap: "slice bounds out of range [:%x]",
|
||||
boundsSliceB: "slice bounds out of range [%x:]",
|
||||
boundsSlice3Alen: "slice bounds out of range [::%x]",
|
||||
boundsSlice3Acap: "slice bounds out of range [::%x]",
|
||||
boundsSlice3B: "slice bounds out of range [:%x:]",
|
||||
boundsSlice3C: "slice bounds out of range [%x::]",
|
||||
}
|
||||
|
||||
func (e boundsError) RuntimeError() {}
|
||||
|
||||
func appendIntStr(b []byte, v int64, signed bool) []byte {
|
||||
if signed && v < 0 {
|
||||
b = append(b, '-')
|
||||
v = -v
|
||||
}
|
||||
var buf [20]byte
|
||||
b = append(b, itoa(buf[:], uint64(v))...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (e boundsError) Error() string {
|
||||
fmt := boundsErrorFmts[e.code]
|
||||
if e.signed && e.x < 0 {
|
||||
fmt = boundsNegErrorFmts[e.code]
|
||||
}
|
||||
// max message length is 99: "runtime error: slice bounds out of range [::%x] with capacity %y"
|
||||
// x can be at most 20 characters. y can be at most 19.
|
||||
b := make([]byte, 0, 100)
|
||||
b = append(b, "runtime error: "...)
|
||||
for i := 0; i < len(fmt); i++ {
|
||||
c := fmt[i]
|
||||
if c != '%' {
|
||||
b = append(b, c)
|
||||
continue
|
||||
}
|
||||
i++
|
||||
switch fmt[i] {
|
||||
case 'x':
|
||||
b = appendIntStr(b, e.x, e.signed)
|
||||
case 'y':
|
||||
b = appendIntStr(b, int64(e.y), true)
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
type stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
@ -23,81 +23,189 @@ import (
|
||||
//go:linkname makefuncreturning runtime.makefuncreturning
|
||||
//go:linkname gorecover runtime.gorecover
|
||||
//go:linkname deferredrecover runtime.deferredrecover
|
||||
//go:linkname goPanicIndex runtime.goPanicIndex
|
||||
//go:linkname goPanicIndexU runtime.goPanicIndexU
|
||||
//go:linkname goPanicSliceAlen runtime.goPanicSliceAlen
|
||||
//go:linkname goPanicSliceAlenU runtime.goPanicSliceAlenU
|
||||
//go:linkname goPanicSliceAcap runtime.goPanicSliceAcap
|
||||
//go:linkname goPanicSliceAcapU runtime.goPanicSliceAcapU
|
||||
//go:linkname goPanicSliceB runtime.goPanicSliceB
|
||||
//go:linkname goPanicSliceBU runtime.goPanicSliceBU
|
||||
//go:linkname goPanicSlice3Alen runtime.goPanicSlice3Alen
|
||||
//go:linkname goPanicSlice3AlenU runtime.goPanicSlice3AlenU
|
||||
//go:linkname goPanicSlice3Acap runtime.goPanicSlice3Acap
|
||||
//go:linkname goPanicSlice3AcapU runtime.goPanicSlice3AcapU
|
||||
//go:linkname goPanicSlice3B runtime.goPanicSlice3B
|
||||
//go:linkname goPanicSlice3BU runtime.goPanicSlice3BU
|
||||
//go:linkname goPanicSlice3C runtime.goPanicSlice3C
|
||||
//go:linkname goPanicSlice3CU runtime.goPanicSlice3CU
|
||||
//go:linkname panicmem runtime.panicmem
|
||||
// Temporary for C code to call:
|
||||
//go:linkname throw runtime.throw
|
||||
|
||||
// Calling panic with one of the errors below will call errorString.Error
|
||||
// which will call mallocgc to concatenate strings. That will fail if
|
||||
// malloc is locked, causing a confusing error message. Throw a better
|
||||
// error message instead.
|
||||
func panicCheckMalloc(err error) {
|
||||
// Check to make sure we can really generate a panic. If the panic
|
||||
// was generated from the runtime, or from inside malloc, then convert
|
||||
// to a throw of msg.
|
||||
// pc should be the program counter of the compiler-generated code that
|
||||
// triggered this panic.
|
||||
func panicCheck1(pc uintptr, msg string) {
|
||||
name, _, _, _ := funcfileline(pc-1, -1)
|
||||
if hasPrefix(name, "runtime.") {
|
||||
throw(msg)
|
||||
}
|
||||
// TODO: is this redundant? How could we be in malloc
|
||||
// but not in the runtime? runtime/internal/*, maybe?
|
||||
gp := getg()
|
||||
if gp != nil && gp.m != nil && gp.m.mallocing != 0 {
|
||||
throw(string(err.(errorString)))
|
||||
throw(msg)
|
||||
}
|
||||
}
|
||||
|
||||
var indexError = error(errorString("index out of range"))
|
||||
// Same as above, but calling from the runtime is allowed.
|
||||
//
|
||||
// Using this function is necessary for any panic that may be
|
||||
// generated by runtime.sigpanic, since those are always called by the
|
||||
// runtime.
|
||||
func panicCheck2(err string) {
|
||||
// panic allocates, so to avoid recursive malloc, turn panics
|
||||
// during malloc into throws.
|
||||
gp := getg()
|
||||
if gp != nil && gp.m != nil && gp.m.mallocing != 0 {
|
||||
throw(err)
|
||||
}
|
||||
}
|
||||
|
||||
// The panicindex, panicslice, and panicdivide functions are called by
|
||||
// Many of the following panic entry-points turn into throws when they
|
||||
// happen in various runtime contexts. These should never happen in
|
||||
// the runtime, and if they do, they indicate a serious issue and
|
||||
// should not be caught by user code.
|
||||
//
|
||||
// The panic{Index,Slice,divide,shift} functions are called by
|
||||
// code generated by the compiler for out of bounds index expressions,
|
||||
// out of bounds slice expressions, and division by zero. The
|
||||
// panicdivide (again), panicoverflow, panicfloat, and panicmem
|
||||
// out of bounds slice expressions, division by zero, and shift by negative.
|
||||
// The panicdivide (again), panicoverflow, panicfloat, and panicmem
|
||||
// functions are called by the signal handler when a signal occurs
|
||||
// indicating the respective problem.
|
||||
//
|
||||
// Since panicindex and panicslice are never called directly, and
|
||||
// Since panic{Index,Slice,shift} are never called directly, and
|
||||
// since the runtime package should never have an out of bounds slice
|
||||
// or array reference, if we see those functions called from the
|
||||
// or array reference or negative shift, if we see those functions called from the
|
||||
// runtime package we turn the panic into a throw. That will dump the
|
||||
// entire runtime stack for easier debugging.
|
||||
//
|
||||
// The entry points called by the signal handler will be called from
|
||||
// runtime.sigpanic, so we can't disallow calls from the runtime to
|
||||
// these (they always look like they're called from the runtime).
|
||||
// Hence, for these, we just check for clearly bad runtime conditions.
|
||||
|
||||
func panicindex() {
|
||||
name, _, _, _ := funcfileline(getcallerpc()-1, -1)
|
||||
if hasPrefix(name, "runtime.") {
|
||||
throw(string(indexError.(errorString)))
|
||||
}
|
||||
panicCheckMalloc(indexError)
|
||||
panic(indexError)
|
||||
// failures in the comparisons for s[x], 0 <= x < y (y == len(s))
|
||||
func goPanicIndex(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "index out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex})
|
||||
}
|
||||
func goPanicIndexU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "index out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex})
|
||||
}
|
||||
|
||||
var sliceError = error(errorString("slice bounds out of range"))
|
||||
// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s))
|
||||
func goPanicSliceAlen(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAlen})
|
||||
}
|
||||
func goPanicSliceAlenU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen})
|
||||
}
|
||||
func goPanicSliceAcap(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAcap})
|
||||
}
|
||||
func goPanicSliceAcapU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap})
|
||||
}
|
||||
|
||||
func panicslice() {
|
||||
name, _, _, _ := funcfileline(getcallerpc()-1, -1)
|
||||
if hasPrefix(name, "runtime.") {
|
||||
throw(string(sliceError.(errorString)))
|
||||
}
|
||||
panicCheckMalloc(sliceError)
|
||||
panic(sliceError)
|
||||
// failures in the comparisons for s[x:y], 0 <= x <= y
|
||||
func goPanicSliceB(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB})
|
||||
}
|
||||
func goPanicSliceBU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s))
|
||||
func goPanicSlice3Alen(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Alen})
|
||||
}
|
||||
func goPanicSlice3AlenU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen})
|
||||
}
|
||||
func goPanicSlice3Acap(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Acap})
|
||||
}
|
||||
func goPanicSlice3AcapU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[:x:y], 0 <= x <= y
|
||||
func goPanicSlice3B(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3B})
|
||||
}
|
||||
func goPanicSlice3BU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[x:y:], 0 <= x <= y
|
||||
func goPanicSlice3C(x int, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3C})
|
||||
}
|
||||
func goPanicSlice3CU(x uint, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C})
|
||||
}
|
||||
|
||||
var shiftError = error(errorString("negative shift amount"))
|
||||
|
||||
func panicshift() {
|
||||
panicCheck1(getcallerpc(), "negative shift amount")
|
||||
panic(shiftError)
|
||||
}
|
||||
|
||||
var divideError = error(errorString("integer divide by zero"))
|
||||
|
||||
func panicdivide() {
|
||||
panicCheckMalloc(divideError)
|
||||
panicCheck2("integer divide by zero")
|
||||
panic(divideError)
|
||||
}
|
||||
|
||||
var overflowError = error(errorString("integer overflow"))
|
||||
|
||||
func panicoverflow() {
|
||||
panicCheckMalloc(overflowError)
|
||||
panicCheck2("integer overflow")
|
||||
panic(overflowError)
|
||||
}
|
||||
|
||||
var floatError = error(errorString("floating point error"))
|
||||
|
||||
func panicfloat() {
|
||||
panicCheckMalloc(floatError)
|
||||
panicCheck2("floating point error")
|
||||
panic(floatError)
|
||||
}
|
||||
|
||||
var memoryError = error(errorString("invalid memory address or nil pointer dereference"))
|
||||
|
||||
func panicmem() {
|
||||
panicCheckMalloc(memoryError)
|
||||
panicCheck2("invalid memory address or nil pointer dereference")
|
||||
panic(memoryError)
|
||||
}
|
||||
|
||||
|
108
libgo/go/runtime/panic32.go
Normal file
108
libgo/go/runtime/panic32.go
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build 386 amd64p32 arm mips mipsle m68k nios2 sh shbe
|
||||
|
||||
package runtime
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
// For gccgo, use go:linkname to rename compiler-called functions to
|
||||
// themselves, so that the compiler will export them.
|
||||
//
|
||||
//go:linkname goPanicExtendIndex runtime.goPanicExtendIndex
|
||||
//go:linkname goPanicExtendIndexU runtime.goPanicExtendIndexU
|
||||
//go:linkname goPanicExtendSliceAlen runtime.goPanicExtendSliceAlen
|
||||
//go:linkname goPanicExtendSliceAlenU runtime.goPanicExtendSliceAlenU
|
||||
//go:linkname goPanicExtendSliceAcap runtime.goPanicExtendSliceAcap
|
||||
//go:linkname goPanicExtendSliceAcapU runtime.goPanicExtendSliceAcapU
|
||||
//go:linkname goPanicExtendSliceB runtime.goPanicExtendSliceB
|
||||
//go:linkname goPanicExtendSliceBU runtime.goPanicExtendSliceBU
|
||||
//go:linkname goPanicExtendSlice3Alen runtime.goPanicExtendSlice3Alen
|
||||
//go:linkname goPanicExtendSlice3AlenU runtime.goPanicExtendSlice3AlenU
|
||||
//go:linkname goPanicExtendSlice3Acap runtime.goPanicExtendSlice3Acap
|
||||
//go:linkname goPanicExtendSlice3AcapU runtime.goPanicExtendSlice3AcapU
|
||||
//go:linkname goPanicExtendSlice3B runtime.goPanicExtendSlice3B
|
||||
//go:linkname goPanicExtendSlice3BU runtime.goPanicExtendSlice3BU
|
||||
//go:linkname goPanicExtendSlice3C runtime.goPanicExtendSlice3C
|
||||
//go:linkname goPanicExtendSlice3CU runtime.goPanicExtendSlice3CU
|
||||
|
||||
// Additional index/slice error paths for 32-bit platforms.
|
||||
// Used when the high word of a 64-bit index is not zero.
|
||||
|
||||
// failures in the comparisons for s[x], 0 <= x < y (y == len(s))
|
||||
func goPanicExtendIndex(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "index out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsIndex})
|
||||
}
|
||||
func goPanicExtendIndexU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "index out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s))
|
||||
func goPanicExtendSliceAlen(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSliceAlen})
|
||||
}
|
||||
func goPanicExtendSliceAlenU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen})
|
||||
}
|
||||
func goPanicExtendSliceAcap(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSliceAcap})
|
||||
}
|
||||
func goPanicExtendSliceAcapU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[x:y], 0 <= x <= y
|
||||
func goPanicExtendSliceB(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSliceB})
|
||||
}
|
||||
func goPanicExtendSliceBU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s))
|
||||
func goPanicExtendSlice3Alen(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3Alen})
|
||||
}
|
||||
func goPanicExtendSlice3AlenU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen})
|
||||
}
|
||||
func goPanicExtendSlice3Acap(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3Acap})
|
||||
}
|
||||
func goPanicExtendSlice3AcapU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[:x:y], 0 <= x <= y
|
||||
func goPanicExtendSlice3B(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3B})
|
||||
}
|
||||
func goPanicExtendSlice3BU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B})
|
||||
}
|
||||
|
||||
// failures in the comparisons for s[x:y:], 0 <= x <= y
|
||||
func goPanicExtendSlice3C(x int64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3C})
|
||||
}
|
||||
func goPanicExtendSlice3CU(x uint64, y int) {
|
||||
panicCheck1(getcallerpc(), "slice bounds out of range")
|
||||
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C})
|
||||
}
|
@ -15,14 +15,15 @@ rm -f runtime.inc.tmp2 runtime.inc.tmp3
|
||||
# types and should not be exported back to C
|
||||
# semt is a Go translation of the C type sem_t; it fails to convert on
|
||||
# some systems and need not be exported back to C.
|
||||
# sigset conflicts with system type sigset on AIX, so we need to rename it
|
||||
# sigset conflicts with system type sigset on AIX, so we need to rename it.
|
||||
# boundsError has a field name that is a C keyword, and we don't need it.
|
||||
|
||||
grep -v "#define _" ${IN} | grep -v "#define [cm][01234] " | grep -v "#define empty " | grep -v "#define \\$" > runtime.inc.tmp2
|
||||
for pattern in '_[GP][a-z]' _Max _Lock _Sig _Trace _MHeap _Num
|
||||
do
|
||||
grep "#define $pattern" ${IN} >> runtime.inc.tmp2
|
||||
done
|
||||
TYPES="_Complex_lock _Reader_lock semt"
|
||||
TYPES="_Complex_lock _Reader_lock semt boundsError"
|
||||
for TYPE in $TYPES
|
||||
do
|
||||
sed -e '/struct '${TYPE}' {/,/^}/s/^.*$//' runtime.inc.tmp2 > runtime.inc.tmp3;
|
||||
|
Loading…
Reference in New Issue
Block a user