| Index: src/preparser.h
|
| diff --git a/src/preparser.h b/src/preparser.h
|
| index cdfceea2608f19a6d6f93bc7804d7288e318fc61..3e4c49d8ca81d6f8c4b9bccf93e5d19f35c2320f 100644
|
| --- a/src/preparser.h
|
| +++ b/src/preparser.h
|
| @@ -115,6 +115,7 @@ class ParserBase : public Traits {
|
| allow_harmony_default_parameters_(false),
|
| allow_harmony_spread_calls_(false),
|
| allow_harmony_destructuring_(false),
|
| + allow_harmony_destructuring_assignment_(false),
|
| allow_harmony_spread_arrays_(false),
|
| allow_harmony_new_target_(false),
|
| allow_strong_mode_(false),
|
| @@ -134,6 +135,7 @@ class ParserBase : public Traits {
|
| ALLOW_ACCESSORS(harmony_default_parameters);
|
| ALLOW_ACCESSORS(harmony_spread_calls);
|
| ALLOW_ACCESSORS(harmony_destructuring);
|
| + ALLOW_ACCESSORS(harmony_destructuring_assignment);
|
| ALLOW_ACCESSORS(harmony_spread_arrays);
|
| ALLOW_ACCESSORS(harmony_new_target);
|
| ALLOW_ACCESSORS(strong_mode);
|
| @@ -549,8 +551,11 @@ class ParserBase : public Traits {
|
| }
|
|
|
| void ValidateExpression(const ExpressionClassifier* classifier, bool* ok) {
|
| - if (!classifier->is_valid_expression()) {
|
| - ReportClassifierError(classifier->expression_error());
|
| + if (!classifier->is_valid_expression() ||
|
| + classifier->has_cover_initialized_name()) {
|
| + ReportClassifierError(ExpressionClassifier::FirdRecorded(
|
| + classifier->expression_error(),
|
| + classifier->cover_initialized_name_error()));
|
| *ok = false;
|
| }
|
| }
|
| @@ -688,6 +693,8 @@ class ParserBase : public Traits {
|
| ExpressionT ParseExpression(bool accept_IN, bool* ok);
|
| ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier,
|
| bool* ok);
|
| + ExpressionT ParseExpression(bool accept_IN, int flags,
|
| + ExpressionClassifier* classifier, bool* ok);
|
| ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok);
|
| ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
|
| bool* is_static, bool* is_computed_name,
|
| @@ -700,8 +707,29 @@ class ParserBase : public Traits {
|
| typename Traits::Type::ExpressionList ParseArguments(
|
| Scanner::Location* first_spread_pos, ExpressionClassifier* classifier,
|
| bool* ok);
|
| +
|
| + enum AssignmentFlags {
|
| + kIsRightHandSide = 1 << 0,
|
| + kIsPatternElement = 1 << 1,
|
| + kMaybeBindingPattern = 1 << 2
|
| + };
|
| ExpressionT ParseAssignmentExpression(bool accept_IN,
|
| ExpressionClassifier* classifier,
|
| + bool* ok) {
|
| + bool seen_destructuring = false;
|
| + return ParseAssignmentExpression(accept_IN, 0, &seen_destructuring,
|
| + classifier, ok);
|
| + }
|
| + ExpressionT ParseAssignmentExpression(bool accept_IN, int flags,
|
| + ExpressionClassifier* classifier,
|
| + bool* ok) {
|
| + bool seen_destructuring = false;
|
| + return ParseAssignmentExpression(accept_IN, flags, &seen_destructuring,
|
| + classifier, ok);
|
| + }
|
| + ExpressionT ParseAssignmentExpression(bool accept_IN, int flags,
|
| + bool* seen_destructuring,
|
| + ExpressionClassifier* classifier,
|
| bool* ok);
|
| ExpressionT ParseYieldExpression(ExpressionClassifier* classifier, bool* ok);
|
| ExpressionT ParseConditionalExpression(bool accept_IN,
|
| @@ -754,6 +782,8 @@ class ParserBase : public Traits {
|
| ExpressionT expression, int beg_pos, int end_pos,
|
| MessageTemplate::Template message, ParseErrorType type, bool* ok);
|
|
|
| + bool IsValidReferenceExpression(ExpressionT expression);
|
| +
|
| // Used to validate property names in object literals and class literals
|
| enum PropertyKind {
|
| kAccessorProperty,
|
| @@ -843,6 +873,7 @@ class ParserBase : public Traits {
|
| bool allow_harmony_default_parameters_;
|
| bool allow_harmony_spread_calls_;
|
| bool allow_harmony_destructuring_;
|
| + bool allow_harmony_destructuring_assignment_;
|
| bool allow_harmony_spread_arrays_;
|
| bool allow_harmony_new_target_;
|
| bool allow_strong_mode_;
|
| @@ -957,6 +988,17 @@ class PreParserExpression {
|
| right->IsSpreadExpression()));
|
| }
|
|
|
| + static PreParserExpression Assignment() {
|
| + return PreParserExpression(TypeField::encode(kAssignmentExpression));
|
| + }
|
| +
|
| + static PreParserExpression ObjectLiteral(
|
| + bool has_cover_initialized_name = false) {
|
| + return PreParserExpression(
|
| + TypeField::encode(kObjectLiteralExpression) |
|
| + HasCoverInitializedNameField::encode(has_cover_initialized_name));
|
| + }
|
| +
|
| static PreParserExpression StringLiteral() {
|
| return PreParserExpression(TypeField::encode(kStringLiteralExpression));
|
| }
|
| @@ -1005,6 +1047,10 @@ class PreParserExpression {
|
| ExpressionTypeField::encode(kNoTemplateTagExpression));
|
| }
|
|
|
| + bool IsAssignment() const {
|
| + return TypeField::decode(code_) == kAssignmentExpression;
|
| + }
|
| +
|
| bool IsIdentifier() const {
|
| return TypeField::decode(code_) == kIdentifierExpression;
|
| }
|
| @@ -1014,6 +1060,19 @@ class PreParserExpression {
|
| return PreParserIdentifier(IdentifierTypeField::decode(code_));
|
| }
|
|
|
| + PreParserExpression AsObjectLiteral() const {
|
| + DCHECK(IsObjectLiteral());
|
| + return *this;
|
| + }
|
| +
|
| + bool IsObjectLiteral() const {
|
| + return TypeField::decode(code_) == kObjectLiteralExpression;
|
| + }
|
| +
|
| + bool IsArrayLiteral() const {
|
| + return TypeField::decode(code_) == kObjectLiteralExpression;
|
| + }
|
| +
|
| bool IsStringLiteral() const {
|
| return TypeField::decode(code_) == kStringLiteralExpression;
|
| }
|
| @@ -1102,7 +1161,9 @@ class PreParserExpression {
|
| kIdentifierExpression,
|
| kStringLiteralExpression,
|
| kBinaryOperationExpression,
|
| - kSpreadExpression
|
| + kSpreadExpression,
|
| + kObjectLiteralExpression,
|
| + kAssignmentExpression
|
| };
|
|
|
| enum ExpressionType {
|
| @@ -1128,6 +1189,7 @@ class PreParserExpression {
|
| typedef BitField<PreParserIdentifier::Type, TypeField::kNext, 10>
|
| IdentifierTypeField;
|
| typedef BitField<bool, TypeField::kNext, 1> HasRestField;
|
| + typedef BitField<bool, TypeField::kNext, 1> HasCoverInitializedNameField;
|
|
|
| uint32_t code_;
|
| };
|
| @@ -1240,12 +1302,12 @@ class PreParserFactory {
|
| int literal_index,
|
| bool is_strong,
|
| int pos) {
|
| - return PreParserExpression::Default();
|
| + return PreParserExpression::ObjectLiteral();
|
| }
|
| PreParserExpression NewArrayLiteral(PreParserExpressionList values,
|
| int first_spread_index, int literal_index,
|
| bool is_strong, int pos) {
|
| - return PreParserExpression::Default();
|
| + return PreParserExpression::ObjectLiteral();
|
| }
|
| PreParserExpression NewObjectLiteralProperty(PreParserExpression key,
|
| PreParserExpression value,
|
| @@ -1266,7 +1328,7 @@ class PreParserFactory {
|
| bool has_function,
|
| bool is_strong,
|
| int pos) {
|
| - return PreParserExpression::Default();
|
| + return PreParserExpression::ObjectLiteral();
|
| }
|
| PreParserExpression NewVariableProxy(void* variable) {
|
| return PreParserExpression::Default();
|
| @@ -1298,7 +1360,7 @@ class PreParserFactory {
|
| PreParserExpression left,
|
| PreParserExpression right,
|
| int pos) {
|
| - return PreParserExpression::Default();
|
| + return PreParserExpression::Assignment();
|
| }
|
| PreParserExpression NewYield(PreParserExpression generator_object,
|
| PreParserExpression expression,
|
| @@ -1730,6 +1792,11 @@ class PreParserTraits {
|
| PreParserExpressionList args,
|
| int pos);
|
|
|
| + inline PreParserExpression RewriteDestructuringAssignmentExpression(
|
| + PreParserExpression expr) {
|
| + return expr;
|
| + }
|
| +
|
| private:
|
| PreParser* pre_parser_;
|
| };
|
| @@ -2375,8 +2442,17 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
|
| // Heuristically try to detect immediately called functions before
|
| // seeing the call parentheses.
|
| parenthesized_function_ = (peek() == Token::FUNCTION);
|
| - result = this->ParseExpression(true, classifier, CHECK_OK);
|
| + result = this->ParseExpression(true, kMaybeBindingPattern, classifier,
|
| + CHECK_OK);
|
| Expect(Token::RPAREN, CHECK_OK);
|
| +
|
| + if (peek() != Token::ARROW) {
|
| + if (allow_harmony_destructuring_assignment()) {
|
| + ValidateAssignmentPattern(classifier, CHECK_OK);
|
| + } else {
|
| + ValidateExpression(classifier, CHECK_OK);
|
| + }
|
| + }
|
| }
|
| break;
|
|
|
| @@ -2450,17 +2526,26 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
|
| }
|
|
|
|
|
| -// Precedence = 1
|
| template <class Traits>
|
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
|
| bool accept_IN, ExpressionClassifier* classifier, bool* ok) {
|
| + ExpressionT result = ParseExpression(accept_IN, 0, classifier, CHECK_OK);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +// Precedence = 1
|
| +template <class Traits>
|
| +typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
|
| + bool accept_IN, int flags, ExpressionClassifier* classifier, bool* ok) {
|
| // Expression ::
|
| // AssignmentExpression
|
| // Expression ',' AssignmentExpression
|
|
|
| ExpressionClassifier binding_classifier;
|
| - ExpressionT result =
|
| - this->ParseAssignmentExpression(accept_IN, &binding_classifier, CHECK_OK);
|
| + bool seen_destructuring = false;
|
| + ExpressionT result = this->ParseAssignmentExpression(
|
| + accept_IN, flags, &seen_destructuring, &binding_classifier, CHECK_OK);
|
| classifier->Accumulate(binding_classifier,
|
| ExpressionClassifier::AllProductions);
|
| bool is_simple_parameter_list = this->IsIdentifier(result);
|
| @@ -2485,7 +2570,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
|
| }
|
| int pos = position();
|
| ExpressionT right = this->ParseAssignmentExpression(
|
| - accept_IN, &binding_classifier, CHECK_OK);
|
| + accept_IN, flags, &seen_destructuring, &binding_classifier, CHECK_OK);
|
| if (is_rest) right = factory()->NewSpread(right, pos);
|
| is_simple_parameter_list =
|
| is_simple_parameter_list && this->IsIdentifier(right);
|
| @@ -2496,6 +2581,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
|
| if (!is_simple_parameter_list || seen_rest) {
|
| classifier->RecordNonSimpleParameter();
|
| }
|
| +
|
| return result;
|
| }
|
|
|
| @@ -2523,20 +2609,43 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
|
| }
|
| elem = this->GetLiteralTheHole(peek_position(), factory());
|
| } else if (peek() == Token::ELLIPSIS) {
|
| - if (!allow_harmony_spread_arrays()) {
|
| + if (!allow_harmony_spread_arrays() && !allow_harmony_destructuring() &&
|
| + !allow_harmony_destructuring_assignment()) {
|
| ExpressionUnexpectedToken(classifier);
|
| }
|
| int start_pos = peek_position();
|
| Consume(Token::ELLIPSIS);
|
| ExpressionT argument =
|
| this->ParseAssignmentExpression(true, classifier, CHECK_OK);
|
| + if ((argument->IsArrayLiteral() || argument->IsObjectLiteral()) &&
|
| + !classifier->is_valid_assignment_pattern()) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(start_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidDestructuringTarget);
|
| + }
|
| elem = factory()->NewSpread(argument, start_pos);
|
| seen_spread = true;
|
| if (first_spread_index < 0) {
|
| first_spread_index = values->length();
|
| }
|
| + if (argument->IsAssignment()) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(start_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidRestInitializer);
|
| + } else if (peek() == Token::COMMA) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(start_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kElementAfterRest);
|
| + }
|
| } else {
|
| - elem = this->ParseAssignmentExpression(true, classifier, CHECK_OK);
|
| + elem = this->ParseAssignmentExpression(true, kIsPatternElement,
|
| + classifier, CHECK_OK);
|
| + if (!this->IsValidReferenceExpression(elem) &&
|
| + !classifier->is_valid_assignment_pattern()) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidDestructuringTarget);
|
| + }
|
| }
|
| values->Add(elem, zone_);
|
| if (peek() != Token::RBRACK) {
|
| @@ -2652,8 +2761,18 @@ ParserBase<Traits>::ParsePropertyDefinition(
|
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
| }
|
| Consume(Token::COLON);
|
| + int pos = peek_position();
|
| value = this->ParseAssignmentExpression(
|
| - true, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
| + true, kIsPatternElement, classifier,
|
| + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
| +
|
| + if (!this->IsValidReferenceExpression(value) &&
|
| + !classifier->is_valid_assignment_pattern()) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidDestructuringTarget);
|
| + }
|
| +
|
| return factory()->NewObjectLiteralProperty(name_expression, value, false,
|
| *is_computed_name);
|
| }
|
| @@ -2681,15 +2800,18 @@ ParserBase<Traits>::ParsePropertyDefinition(
|
| name, next_beg_pos, next_end_pos, scope_, factory());
|
|
|
| if (peek() == Token::ASSIGN) {
|
| - this->ExpressionUnexpectedToken(classifier);
|
| Consume(Token::ASSIGN);
|
| ExpressionClassifier rhs_classifier;
|
| ExpressionT rhs = this->ParseAssignmentExpression(
|
| - true, &rhs_classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
| + true, kIsRightHandSide, &rhs_classifier,
|
| + CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
| classifier->Accumulate(rhs_classifier,
|
| ExpressionClassifier::ExpressionProductions);
|
| value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs,
|
| RelocInfo::kNoPosition);
|
| + classifier->RecordCoverInitializedNameError(
|
| + Scanner::Location(next_beg_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidCoverInitializedName);
|
| } else {
|
| value = lhs;
|
| }
|
| @@ -2700,6 +2822,11 @@ ParserBase<Traits>::ParsePropertyDefinition(
|
| }
|
| }
|
|
|
| + // Method definitions are never valid in patterns.
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(next_beg_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidDestructuringTarget);
|
| +
|
|
|
| if (is_generator || peek() == Token::LPAREN) {
|
| // MethodDefinition
|
| @@ -2925,7 +3052,8 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
|
| // Precedence = 2
|
| template <class Traits>
|
| typename ParserBase<Traits>::ExpressionT
|
| -ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
| +ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, int flags,
|
| + bool* seen_destructuring,
|
| ExpressionClassifier* classifier,
|
| bool* ok) {
|
| // AssignmentExpression ::
|
| @@ -2935,6 +3063,12 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
| // LeftHandSideExpression AssignmentOperator AssignmentExpression
|
|
|
| int lhs_beg_pos = peek_position();
|
| + bool is_rhs = flags & kIsRightHandSide;
|
| + bool is_pattern_element = flags & kIsPatternElement;
|
| + bool maybe_binding_pattern = flags & kMaybeBindingPattern;
|
| + USE(maybe_binding_pattern);
|
| + flags =
|
| + flags & ~(kIsPatternElement | kMaybeBindingPattern) | kIsRightHandSide;
|
|
|
| if (peek() == Token::YIELD && is_generator()) {
|
| return this->ParseYieldExpression(classifier, ok);
|
| @@ -2975,18 +3109,37 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
| }
|
| expression = this->ParseArrowFunctionLiteral(
|
| accept_IN, parameters, arrow_formals_classifier, CHECK_OK);
|
| + if (is_pattern_element) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(lhs_beg_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidDestructuringTarget);
|
| + }
|
| return expression;
|
| }
|
|
|
| // "expression" was not itself an arrow function parameter list, but it might
|
| // form part of one. Propagate speculative formal parameter error locations.
|
| - classifier->Accumulate(arrow_formals_classifier,
|
| - ExpressionClassifier::StandardProductions |
|
| - ExpressionClassifier::FormalParametersProductions);
|
| + classifier->Accumulate(
|
| + arrow_formals_classifier,
|
| + ExpressionClassifier::StandardProductions |
|
| + ExpressionClassifier::FormalParametersProductions |
|
| + ExpressionClassifier::CoverInitializedNameProduction);
|
| +
|
| + bool maybe_pattern =
|
| + expression->IsObjectLiteral() || expression->IsArrayLiteral();
|
|
|
| if (!Token::IsAssignmentOp(peek())) {
|
| if (fni_ != NULL) fni_->Leave();
|
| // Parsed conditional expression only (no assignment).
|
| + if (is_pattern_element && !this->IsValidReferenceExpression(expression) &&
|
| + !maybe_pattern) {
|
| + classifier->RecordPatternError(
|
| + Scanner::Location(lhs_beg_pos, scanner()->location().end_pos),
|
| + MessageTemplate::kInvalidDestructuringTarget);
|
| + } else if (is_rhs && !is_pattern_element && maybe_pattern) {
|
| + ValidateExpression(classifier, CHECK_OK);
|
| + }
|
| +
|
| return expression;
|
| }
|
|
|
| @@ -2994,9 +3147,22 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
| BindingPatternUnexpectedToken(classifier);
|
| }
|
|
|
| - expression = this->CheckAndRewriteReferenceExpression(
|
| - expression, lhs_beg_pos, scanner()->location().end_pos,
|
| - MessageTemplate::kInvalidLhsInAssignment, CHECK_OK);
|
| + bool maybe_destructuring_assignment =
|
| + allow_harmony_destructuring_assignment() && maybe_pattern &&
|
| + peek() == Token::ASSIGN;
|
| + bool maybe_destructuring_binding = allow_harmony_destructuring() &&
|
| + maybe_pattern && !is_rhs &&
|
| + peek() == Token::ASSIGN;
|
| +
|
| + if (maybe_destructuring_assignment) {
|
| + classifier->ForgiveCoverInitializedNameError();
|
| + ValidateAssignmentPattern(classifier, CHECK_OK);
|
| + if (!is_pattern_element) *seen_destructuring = true;
|
| + } else if (!maybe_destructuring_binding) {
|
| + expression = this->CheckAndRewriteReferenceExpression(
|
| + expression, lhs_beg_pos, scanner()->location().end_pos,
|
| + MessageTemplate::kInvalidLhsInAssignment, CHECK_OK);
|
| + }
|
| expression = this->MarkExpressionAsAssigned(expression);
|
|
|
| Token::Value op = Next(); // Get assignment operator.
|
| @@ -3008,10 +3174,11 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
| int pos = position();
|
|
|
| ExpressionClassifier rhs_classifier;
|
| - ExpressionT right =
|
| - this->ParseAssignmentExpression(accept_IN, &rhs_classifier, CHECK_OK);
|
| - classifier->Accumulate(rhs_classifier,
|
| - ExpressionClassifier::ExpressionProductions);
|
| + ExpressionT right = this->ParseAssignmentExpression(
|
| + accept_IN, flags, seen_destructuring, &rhs_classifier, CHECK_OK);
|
| + classifier->Accumulate(
|
| + rhs_classifier, ExpressionClassifier::ExpressionProductions |
|
| + ExpressionClassifier::CoverInitializedNameProduction);
|
|
|
| // TODO(1231235): We try to estimate the set of properties set by
|
| // constructors. We define a new property whenever there is an
|
| @@ -3039,7 +3206,14 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
| fni_->Leave();
|
| }
|
|
|
| - return factory()->NewAssignment(op, expression, right, pos);
|
| + ExpressionT result = factory()->NewAssignment(op, expression, right, pos);
|
| +
|
| + if (!is_rhs && *seen_destructuring && !maybe_binding_pattern &&
|
| + !is_pattern_element) {
|
| + return Traits::RewriteDestructuringAssignmentExpression(result);
|
| + }
|
| +
|
| + return result;
|
| }
|
|
|
| template <class Traits>
|
| @@ -4160,6 +4334,26 @@ ParserBase<Traits>::CheckAndRewriteReferenceExpression(
|
| }
|
|
|
|
|
| +template <typename Traits>
|
| +bool ParserBase<Traits>::IsValidReferenceExpression(ExpressionT expression) {
|
| + // TODO(caitp): do this in a less redundant way :(
|
| + if (expression->IsValidReferenceExpression()) {
|
| + if (this->IsIdentifier(expression)) {
|
| + if (is_strict(language_mode()) &&
|
| + this->IsEvalOrArguments(this->AsIdentifier(expression))) {
|
| + return false;
|
| + }
|
| + if (is_strong(language_mode()) &&
|
| + this->IsUndefined(this->AsIdentifier(expression))) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| #undef CHECK_OK
|
| #undef CHECK_OK_CUSTOM
|
|
|
|
|