Skip to content

Commit 6d86c4b

Browse files
lvidacsgalpeter
authored andcommitted
Implement String.prototype.substring()
JerryScript-DCO-1.0-Signed-off-by: Laszlo Vidacs [email protected]
1 parent ddc3f0d commit 6d86c4b

File tree

4 files changed

+245
-1
lines changed

4 files changed

+245
-1
lines changed

jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,47 @@ ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */
325325
return norm_index;
326326
} /* ecma_builtin_helper_array_index_normalize */
327327

328+
/**
329+
* Helper function to normalizing a string index
330+
*
331+
* This function clamps the given index to the [0, length] range.
332+
* If the index is negative, 0 value is used.
333+
* If the index is greater than the length of the string, the normalized index will be the length of the string.
334+
*
335+
* See also:
336+
* ECMA-262 v5, 15.5.4.15
337+
*
338+
* Used by:
339+
* - The String.prototype.substring routine.
340+
*
341+
* @return uint32_t - the normalized value of the index
342+
*/
343+
uint32_t
344+
ecma_builtin_helper_string_index_normalize (ecma_number_t index, /**< index */
345+
uint32_t length) /**< string's length */
346+
{
347+
uint32_t norm_index = 0;
348+
349+
if (!ecma_number_is_nan (index) && !ecma_number_is_negative (index))
350+
{
351+
if (ecma_number_is_infinity (index))
352+
{
353+
norm_index = length;
354+
}
355+
else
356+
{
357+
norm_index = ecma_number_to_uint32 (index);
358+
359+
if (norm_index > length)
360+
{
361+
norm_index = length;
362+
}
363+
}
364+
}
365+
366+
return norm_index;
367+
} /* ecma_builtin_helper_string_index_normalize */
368+
328369
/**
329370
* @}
330371
* @}

jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern ecma_completion_value_t ecma_builtin_helper_get_to_locale_string_at_index
3131
extern ecma_completion_value_t ecma_builtin_helper_object_get_properties (ecma_object_t *obj,
3232
bool only_enumerable_properties);
3333
extern uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length);
34+
extern uint32_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length);
3435

3536
#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_DATE_BUILTIN
3637
/* ecma-builtin-helpers-date.cpp */

jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,69 @@ ecma_builtin_string_prototype_object_substring (ecma_value_t this_arg, /**< this
442442
ecma_value_t arg1, /**< routine's first argument */
443443
ecma_value_t arg2) /**< routine's second argument */
444444
{
445-
ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg1, arg2);
445+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
446+
447+
/* 1 */
448+
ECMA_TRY_CATCH (check_coercible_val,
449+
ecma_op_check_object_coercible (this_arg),
450+
ret_value);
451+
452+
/* 2 */
453+
ECMA_TRY_CATCH (to_string_val,
454+
ecma_op_to_string (this_arg),
455+
ret_value);
456+
457+
/* 3 */
458+
ecma_string_t *original_string_p = ecma_get_string_from_value (to_string_val);
459+
460+
const ecma_length_t len = ecma_string_get_length (original_string_p);
461+
462+
/* 4, 6 */
463+
ECMA_OP_TO_NUMBER_TRY_CATCH (start_num,
464+
arg1,
465+
ret_value);
466+
467+
ecma_length_t start = 0, end = len;
468+
469+
start = ecma_builtin_helper_string_index_normalize (start_num, len);
470+
471+
/* 5, 7 */
472+
if (ecma_is_value_undefined (arg2))
473+
{
474+
end = len;
475+
}
476+
else
477+
{
478+
ECMA_OP_TO_NUMBER_TRY_CATCH (end_num,
479+
arg2,
480+
ret_value);
481+
482+
end = ecma_builtin_helper_string_index_normalize (end_num, len);
483+
484+
ECMA_OP_TO_NUMBER_FINALIZE (end_num);
485+
}
486+
487+
if (ecma_is_completion_value_empty (ret_value))
488+
{
489+
JERRY_ASSERT (start <= len && end <= len);
490+
491+
/* 8 */
492+
uint32_t from = start < end ? start : end;
493+
494+
/* 9 */
495+
uint32_t to = start > end ? start : end;
496+
497+
/* 10 */
498+
ecma_string_t *new_str_p = ecma_string_substr (original_string_p, from, to);
499+
ret_value = ecma_make_normal_completion_value (ecma_make_string_value (new_str_p));
500+
}
501+
502+
ECMA_OP_TO_NUMBER_FINALIZE (start_num);
503+
504+
ECMA_FINALIZE (to_string_val);
505+
ECMA_FINALIZE (check_coercible_val);
506+
507+
return ret_value;
446508
} /* ecma_builtin_string_prototype_object_substring */
447509

448510
/**
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2015 Samsung Electronics Co., Ltd.
2+
// Copyright 2015 University of Szeged.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
// check properties
17+
assert(Object.getOwnPropertyDescriptor(String.prototype.substring, 'length').configurable === false);
18+
19+
assert(Object.getOwnPropertyDescriptor(String.prototype.substring, 'length').enumerable === false);
20+
21+
assert(Object.getOwnPropertyDescriptor(String.prototype.substring, 'length').writable === false);
22+
23+
assert(String.prototype.substring.length === 2);
24+
25+
assert(String.prototype.substring.call(new String()) === "");
26+
27+
assert(String.prototype.substring.call({}) === "[object Object]");
28+
29+
// check this is undefined
30+
try {
31+
String.prototype.substring.call(undefined);
32+
assert(false);
33+
} catch(e) {
34+
assert(e instanceof TypeError);
35+
}
36+
37+
// check this is null
38+
try {
39+
String.prototype.substring.call(null);
40+
assert(false);
41+
} catch(e) {
42+
assert(e instanceof TypeError);
43+
}
44+
45+
// simple checks
46+
assert("hello world!".substring(0, 11) === "hello world");
47+
48+
assert("hello world!".substring(11, 0) === "hello world");
49+
50+
assert("hello world!".substring(0, 12) === "hello world!");
51+
52+
assert("hello world!".substring(12, 0) === "hello world!");
53+
54+
// check NaN
55+
assert("hello world!".substring(NaN, 12) === "hello world!");
56+
57+
// check NaN
58+
assert("hello world!".substring(2, NaN) === "he");
59+
60+
// check end undefined
61+
assert("hello world!".substring(2, undefined) === "llo world!");
62+
63+
// check negative
64+
assert("hello world!".substring(-1,8) === "hello wo");
65+
66+
// check negative
67+
assert("hello\tworld!".substring(5,-8) === "hello");
68+
69+
// check negative
70+
assert("hello world!".substring(-1,-8) === "");
71+
72+
// check ranges
73+
assert("hello world!".substring(-1,10000) === "hello world!");
74+
75+
assert("hello world!".substring(10000,1000000) === "");
76+
77+
assert("hello world!".substring(100000,1) === "ello world!");
78+
79+
// check both undefined
80+
assert("hello world!".substring(undefined, undefined) === "hello world!");
81+
82+
var undef_var;
83+
assert("hello world!".substring(undef_var, undef_var) === "hello world!");
84+
85+
// check integer conversion
86+
assert("hello world!".substring(undefined, 5) === "hello");
87+
88+
assert("hello world!".substring(undefined, "bar") === "");
89+
90+
assert("hello world!".substring(2, true) === "e");
91+
92+
assert("hello world!".substring(2, false) === "he");
93+
94+
assert("hello world!".substring(5, obj) === " world!");
95+
96+
// check other objects
97+
var obj = { substring : String.prototype.substring }
98+
99+
obj.toString = function() {
100+
return "Iam";
101+
}
102+
assert(obj.substring(100000,1) === "am");
103+
104+
obj.toString = function() {
105+
throw new ReferenceError ("foo");
106+
};
107+
108+
try {
109+
assert(obj.substring(100000,1));
110+
assert(false);
111+
} catch (e) {
112+
assert(e.message === "foo");
113+
assert(e instanceof ReferenceError);
114+
}
115+
116+
// check coercible - undefined
117+
try {
118+
assert(true.substring() === "");
119+
assert(false);
120+
} catch (e) {
121+
assert(e instanceof TypeError);
122+
}
123+
124+
// check coercible - null
125+
try {
126+
assert(String.prototype.substring.call(null, 0, 1) === "");
127+
assert(false);
128+
} catch (e) {
129+
assert(e instanceof TypeError);
130+
}
131+
132+
// check coercible - Boolean
133+
assert(String.prototype.substring.call(true, 0, 1) === "t");
134+
135+
// check coercible - Object
136+
var test_object = {firstName:"John", lastName:"Doe"};
137+
assert(String.prototype.substring.call(test_object, 0, 7) === "[object");
138+
139+
// check coercible - Number
140+
assert(String.prototype.substring.call(123, 0, 3) === "123");

0 commit comments

Comments
 (0)