summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp
blob: fa07dcd9cddfea20bf9f2a82620e045b2e491f37 (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
/*
 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/StringBuilder.h>
#include <AK/Utf16View.h>
#include <AK/Utf32View.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/StringConstructor.h>
#include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/Utf16String.h>

namespace JS {

StringConstructor::StringConstructor(Realm& realm)
    : NativeFunction(vm().names.String.as_string(), *realm.global_object().function_prototype())
{
}

void StringConstructor::initialize(Realm& realm)
{
    auto& vm = this->vm();
    NativeFunction::initialize(realm);

    // 22.1.2.3 String.prototype, https://tc39.es/ecma262/#sec-string.prototype
    define_direct_property(vm.names.prototype, realm.global_object().string_prototype(), 0);

    u8 attr = Attribute::Writable | Attribute::Configurable;
    define_native_function(realm, vm.names.raw, raw, 1, attr);
    define_native_function(realm, vm.names.fromCharCode, from_char_code, 1, attr);
    define_native_function(realm, vm.names.fromCodePoint, from_code_point, 1, attr);

    define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}

// 22.1.1.1 String ( value ), https://tc39.es/ecma262/#sec-string-constructor-string-value
ThrowCompletionOr<Value> StringConstructor::call()
{
    auto& vm = this->vm();
    if (!vm.argument_count())
        return js_string(heap(), "");
    if (vm.argument(0).is_symbol())
        return js_string(vm, vm.argument(0).as_symbol().to_string());
    return TRY(vm.argument(0).to_primitive_string(vm));
}

// 22.1.1.1 String ( value ), https://tc39.es/ecma262/#sec-string-constructor-string-value
ThrowCompletionOr<Object*> StringConstructor::construct(FunctionObject& new_target)
{
    auto& vm = this->vm();
    auto& realm = *vm.current_realm();

    PrimitiveString* primitive_string;
    if (!vm.argument_count())
        primitive_string = js_string(vm, "");
    else
        primitive_string = TRY(vm.argument(0).to_primitive_string(vm));
    auto* prototype = TRY(get_prototype_from_constructor(vm, new_target, &GlobalObject::string_prototype));
    return StringObject::create(realm, *primitive_string, *prototype);
}

// 22.1.2.4 String.raw ( template, ...substitutions ), https://tc39.es/ecma262/#sec-string.raw
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::raw)
{
    auto* cooked = TRY(vm.argument(0).to_object(vm));
    auto raw_value = TRY(cooked->get(vm.names.raw));
    auto* raw = TRY(raw_value.to_object(vm));
    auto literal_segments = TRY(length_of_array_like(vm, *raw));

    if (literal_segments == 0)
        return js_string(vm, "");

    auto const number_of_substituions = vm.argument_count() - 1;

    StringBuilder builder;
    for (size_t i = 0; i < literal_segments; ++i) {
        auto next_key = String::number(i);
        auto next_segment_value = TRY(raw->get(next_key));
        auto next_segment = TRY(next_segment_value.to_string(vm));

        builder.append(next_segment);

        if (i + 1 == literal_segments)
            break;

        if (i < number_of_substituions) {
            auto next = vm.argument(i + 1);
            auto next_sub = TRY(next.to_string(vm));
            builder.append(next_sub);
        }
    }
    return js_string(vm, builder.build());
}

// 22.1.2.1 String.fromCharCode ( ...codeUnits ), https://tc39.es/ecma262/#sec-string.fromcharcode
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_char_code)
{
    Vector<u16, 1> string;
    string.ensure_capacity(vm.argument_count());

    for (size_t i = 0; i < vm.argument_count(); ++i)
        string.append(TRY(vm.argument(i).to_u16(vm)));

    return js_string(vm, Utf16String(move(string)));
}

// 22.1.2.2 String.fromCodePoint ( ...codePoints ), https://tc39.es/ecma262/#sec-string.fromcodepoint
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_code_point)
{
    Vector<u16, 1> string;
    string.ensure_capacity(vm.argument_count()); // This will be an under-estimate if any code point is > 0xffff.

    for (size_t i = 0; i < vm.argument_count(); ++i) {
        auto next_code_point = TRY(vm.argument(i).to_number(vm));
        if (!next_code_point.is_integral_number())
            return vm.throw_completion<RangeError>(ErrorType::InvalidCodePoint, next_code_point.to_string_without_side_effects());
        auto code_point = TRY(next_code_point.to_i32(vm));
        if (code_point < 0 || code_point > 0x10FFFF)
            return vm.throw_completion<RangeError>(ErrorType::InvalidCodePoint, next_code_point.to_string_without_side_effects());

        AK::code_point_to_utf16(string, static_cast<u32>(code_point));
    }

    return js_string(vm, Utf16String(move(string)));
}

}