summaryrefslogtreecommitdiff
path: root/chromium/v8/src/objects/option-utils.cc
blob: 234dcdb172a7498a8072492da6ba872f650d3593 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/objects/option-utils.h"

#include "src/numbers/conversions.h"
#include "src/objects/objects-inl.h"

namespace v8 {
namespace internal {

// ecma402/#sec-getoptionsobject
MaybeHandle<JSReceiver> GetOptionsObject(Isolate* isolate,
                                         Handle<Object> options,
                                         const char* method_name) {
  // 1. If options is undefined, then
  if (options->IsUndefined(isolate)) {
    // a. Return ! ObjectCreate(null).
    return isolate->factory()->NewJSObjectWithNullProto();
  }
  // 2. If Type(options) is Object, then
  if (options->IsJSReceiver()) {
    // a. Return options.
    return Handle<JSReceiver>::cast(options);
  }
  // 3. Throw a TypeError exception.
  THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kInvalidArgument),
                  JSReceiver);
}

// ecma402/#sec-coerceoptionstoobject
MaybeHandle<JSReceiver> CoerceOptionsToObject(Isolate* isolate,
                                              Handle<Object> options,
                                              const char* method_name) {
  // 1. If options is undefined, then
  if (options->IsUndefined(isolate)) {
    // a. Return ! ObjectCreate(null).
    return isolate->factory()->NewJSObjectWithNullProto();
  }
  // 2. Return ? ToObject(options).
  ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
                             Object::ToObject(isolate, options, method_name),
                             JSReceiver);
  return Handle<JSReceiver>::cast(options);
}

Maybe<bool> GetStringOption(Isolate* isolate, Handle<JSReceiver> options,
                            const char* property,
                            const std::vector<const char*>& values,
                            const char* method_name,
                            std::unique_ptr<char[]>* result) {
  Handle<String> property_str =
      isolate->factory()->NewStringFromAsciiChecked(property);

  // 1. Let value be ? Get(options, property).
  Handle<Object> value;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value,
      Object::GetPropertyOrElement(isolate, options, property_str),
      Nothing<bool>());

  if (value->IsUndefined(isolate)) {
    return Just(false);
  }

  // 2. c. Let value be ? ToString(value).
  Handle<String> value_str;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value_str, Object::ToString(isolate, value), Nothing<bool>());
  std::unique_ptr<char[]> value_cstr = value_str->ToCString();

  // 2. d. if values is not undefined, then
  if (values.size() > 0) {
    // 2. d. i. If values does not contain an element equal to value,
    // throw a RangeError exception.
    for (size_t i = 0; i < values.size(); i++) {
      if (strcmp(values.at(i), value_cstr.get()) == 0) {
        // 2. e. return value
        *result = std::move(value_cstr);
        return Just(true);
      }
    }

    Handle<String> method_str =
        isolate->factory()->NewStringFromAsciiChecked(method_name);
    THROW_NEW_ERROR_RETURN_VALUE(
        isolate,
        NewRangeError(MessageTemplate::kValueOutOfRange, value, method_str,
                      property_str),
        Nothing<bool>());
  }

  // 2. e. return value
  *result = std::move(value_cstr);
  return Just(true);
}

V8_WARN_UNUSED_RESULT Maybe<bool> GetBoolOption(Isolate* isolate,
                                                Handle<JSReceiver> options,
                                                const char* property,
                                                const char* method_name,
                                                bool* result) {
  Handle<String> property_str =
      isolate->factory()->NewStringFromAsciiChecked(property);

  // 1. Let value be ? Get(options, property).
  Handle<Object> value;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value,
      Object::GetPropertyOrElement(isolate, options, property_str),
      Nothing<bool>());

  // 2. If value is not undefined, then
  if (!value->IsUndefined(isolate)) {
    // 2. b. i. Let value be ToBoolean(value).
    *result = value->BooleanValue(isolate);

    // 2. e. return value
    return Just(true);
  }

  return Just(false);
}

// ecma402/#sec-defaultnumberoption
Maybe<int> DefaultNumberOption(Isolate* isolate, Handle<Object> value, int min,
                               int max, int fallback, Handle<String> property) {
  // 2. Else, return fallback.
  if (value->IsUndefined()) return Just(fallback);

  // 1. If value is not undefined, then
  // a. Let value be ? ToNumber(value).
  Handle<Object> value_num;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value_num, Object::ToNumber(isolate, value), Nothing<int>());
  DCHECK(value_num->IsNumber());

  // b. If value is NaN or less than minimum or greater than maximum, throw a
  // RangeError exception.
  if (value_num->IsNaN() || value_num->Number() < min ||
      value_num->Number() > max) {
    THROW_NEW_ERROR_RETURN_VALUE(
        isolate,
        NewRangeError(MessageTemplate::kPropertyValueOutOfRange, property),
        Nothing<int>());
  }

  // The max and min arguments are integers and the above check makes
  // sure that we are within the integer range making this double to
  // int conversion safe.
  //
  // c. Return floor(value).
  return Just(FastD2I(floor(value_num->Number())));
}

// ecma402/#sec-getnumberoption
Maybe<int> GetNumberOption(Isolate* isolate, Handle<JSReceiver> options,
                           Handle<String> property, int min, int max,
                           int fallback) {
  // 1. Let value be ? Get(options, property).
  Handle<Object> value;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value, JSReceiver::GetProperty(isolate, options, property),
      Nothing<int>());

  // Return ? DefaultNumberOption(value, minimum, maximum, fallback).
  return DefaultNumberOption(isolate, value, min, max, fallback, property);
}

// #sec-getoption while type is "number"
Maybe<double> GetNumberOptionAsDouble(Isolate* isolate,
                                      Handle<JSReceiver> options,
                                      Handle<String> property,
                                      double default_value) {
  // 1. Let value be ? Get(options, property).
  Handle<Object> value;
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value, JSReceiver::GetProperty(isolate, options, property),
      Nothing<double>());
  // 2. If value is undefined, then
  if (value->IsUndefined()) {
    // b. Return default.
    return Just(default_value);
  }
  // 4. Else if type is "number", then
  // a. Set value to ? ToNumber(value).
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
      isolate, value, Object::ToNumber(isolate, value), Nothing<double>());
  // b. If value is NaN, throw a RangeError exception.
  if (value->IsNaN()) {
    THROW_NEW_ERROR_RETURN_VALUE(
        isolate,
        NewRangeError(MessageTemplate::kPropertyValueOutOfRange, property),
        Nothing<double>());
  }

  // 7. Return value.
  return Just(value->Number());
}

}  // namespace internal
}  // namespace v8