1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_INTL_SUPPORT
6 #error Internationalization is expected to be enabled.
7 #endif  // V8_INTL_SUPPORT
8 
9 #include "src/builtins/builtins-iterator-gen.h"
10 #include "src/builtins/builtins-utils-gen.h"
11 #include "src/codegen/code-stub-assembler.h"
12 #include "src/objects/js-list-format-inl.h"
13 #include "src/objects/js-list-format.h"
14 #include "src/objects/objects-inl.h"
15 #include "src/objects/objects.h"
16 
17 namespace v8 {
18 namespace internal {
19 
20 class IntlBuiltinsAssembler : public CodeStubAssembler {
21  public:
IntlBuiltinsAssembler(compiler::CodeAssemblerState * state)22   explicit IntlBuiltinsAssembler(compiler::CodeAssemblerState* state)
23       : CodeStubAssembler(state) {}
24 
25   void ListFormatCommon(TNode<Context> context, TNode<Int32T> argc,
26                         Runtime::FunctionId format_func_id,
27                         const char* method_name);
28 
29   TNode<JSArray> AllocateEmptyJSArray(TNode<Context> context);
30 
PointerToSeqStringData(TNode<String> seq_string)31   TNode<IntPtrT> PointerToSeqStringData(TNode<String> seq_string) {
32     CSA_ASSERT(this,
33                IsSequentialStringInstanceType(LoadInstanceType(seq_string)));
34     STATIC_ASSERT(SeqOneByteString::kHeaderSize ==
35                   SeqTwoByteString::kHeaderSize);
36     return IntPtrAdd(
37         BitcastTaggedToWord(seq_string),
38         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
39   }
40 };
41 
TF_BUILTIN(StringToLowerCaseIntl,IntlBuiltinsAssembler)42 TF_BUILTIN(StringToLowerCaseIntl, IntlBuiltinsAssembler) {
43   const TNode<String> string = CAST(Parameter(Descriptor::kString));
44 
45   Label call_c(this), return_string(this), runtime(this, Label::kDeferred);
46 
47   // Early exit on empty strings.
48   const TNode<Uint32T> length = LoadStringLengthAsWord32(string);
49   GotoIf(Word32Equal(length, Uint32Constant(0)), &return_string);
50 
51   // Unpack strings if possible, and bail to runtime unless we get a one-byte
52   // flat string.
53   ToDirectStringAssembler to_direct(
54       state(), string, ToDirectStringAssembler::kDontUnpackSlicedStrings);
55   to_direct.TryToDirect(&runtime);
56 
57   const TNode<Int32T> instance_type = to_direct.instance_type();
58   CSA_ASSERT(this,
59              Word32BinaryNot(IsIndirectStringInstanceType(instance_type)));
60   GotoIfNot(IsOneByteStringInstanceType(instance_type), &runtime);
61 
62   // For short strings, do the conversion in CSA through the lookup table.
63 
64   const TNode<String> dst = AllocateSeqOneByteString(length);
65 
66   const int kMaxShortStringLength = 24;  // Determined empirically.
67   GotoIf(Uint32GreaterThan(length, Uint32Constant(kMaxShortStringLength)),
68          &call_c);
69 
70   {
71     const TNode<IntPtrT> dst_ptr = PointerToSeqStringData(dst);
72     TVARIABLE(IntPtrT, var_cursor, IntPtrConstant(0));
73 
74     const TNode<IntPtrT> start_address =
75         ReinterpretCast<IntPtrT>(to_direct.PointerToData(&call_c));
76     const TNode<IntPtrT> end_address =
77         Signed(IntPtrAdd(start_address, ChangeUint32ToWord(length)));
78 
79     const TNode<ExternalReference> to_lower_table_addr =
80         ExternalConstant(ExternalReference::intl_to_latin1_lower_table());
81 
82     TVARIABLE(Word32T, var_did_change, Int32Constant(0));
83 
84     VariableList push_vars({&var_cursor, &var_did_change}, zone());
85     BuildFastLoop<IntPtrT>(
86         push_vars, start_address, end_address,
87         [&](TNode<IntPtrT> current) {
88           TNode<Uint8T> c = Load<Uint8T>(current);
89           TNode<Uint8T> lower =
90               Load<Uint8T>(to_lower_table_addr, ChangeInt32ToIntPtr(c));
91           StoreNoWriteBarrier(MachineRepresentation::kWord8, dst_ptr,
92                               var_cursor.value(), lower);
93 
94           var_did_change =
95               Word32Or(Word32NotEqual(c, lower), var_did_change.value());
96 
97           Increment(&var_cursor);
98         },
99         kCharSize, IndexAdvanceMode::kPost);
100 
101     // Return the original string if it remained unchanged in order to preserve
102     // e.g. internalization and private symbols (such as the preserved object
103     // hash) on the source string.
104     GotoIfNot(var_did_change.value(), &return_string);
105 
106     Return(dst);
107   }
108 
109   // Call into C for case conversion. The signature is:
110   // String ConvertOneByteToLower(String src, String dst);
111   BIND(&call_c);
112   {
113     const TNode<String> src = to_direct.string();
114 
115     const TNode<ExternalReference> function_addr =
116         ExternalConstant(ExternalReference::intl_convert_one_byte_to_lower());
117 
118     MachineType type_tagged = MachineType::AnyTagged();
119 
120     const TNode<String> result = CAST(CallCFunction(
121         function_addr, type_tagged, std::make_pair(type_tagged, src),
122         std::make_pair(type_tagged, dst)));
123 
124     Return(result);
125   }
126 
127   BIND(&return_string);
128   Return(string);
129 
130   BIND(&runtime);
131   {
132     const TNode<Object> result = CallRuntime(Runtime::kStringToLowerCaseIntl,
133                                              NoContextConstant(), string);
134     Return(result);
135   }
136 }
137 
TF_BUILTIN(StringPrototypeToLowerCaseIntl,IntlBuiltinsAssembler)138 TF_BUILTIN(StringPrototypeToLowerCaseIntl, IntlBuiltinsAssembler) {
139   TNode<Object> maybe_string = CAST(Parameter(Descriptor::kReceiver));
140   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
141 
142   TNode<String> string =
143       ToThisString(context, maybe_string, "String.prototype.toLowerCase");
144 
145   Return(CallBuiltin(Builtins::kStringToLowerCaseIntl, context, string));
146 }
147 
ListFormatCommon(TNode<Context> context,TNode<Int32T> argc,Runtime::FunctionId format_func_id,const char * method_name)148 void IntlBuiltinsAssembler::ListFormatCommon(TNode<Context> context,
149                                              TNode<Int32T> argc,
150                                              Runtime::FunctionId format_func_id,
151                                              const char* method_name) {
152   CodeStubArguments args(this, argc);
153 
154   // Label has_list(this);
155   // 1. Let lf be this value.
156   // 2. If Type(lf) is not Object, throw a TypeError exception.
157   TNode<Object> receiver = args.GetReceiver();
158 
159   // 3. If lf does not have an [[InitializedListFormat]] internal slot, throw a
160   // TypeError exception.
161   ThrowIfNotInstanceType(context, receiver, JS_LIST_FORMAT_TYPE, method_name);
162   TNode<JSListFormat> list_format = CAST(receiver);
163 
164   TNode<Object> list = args.GetOptionalArgumentValue(0);
165   {
166     // 4. Let stringList be ? StringListFromIterable(list).
167     TNode<Object> string_list =
168         CallBuiltin(Builtins::kStringListFromIterable, context, list);
169 
170     // 6. Return ? FormatList(lf, stringList).
171     args.PopAndReturn(
172         CallRuntime(format_func_id, context, list_format, string_list));
173   }
174 }
175 
AllocateEmptyJSArray(TNode<Context> context)176 TNode<JSArray> IntlBuiltinsAssembler::AllocateEmptyJSArray(
177     TNode<Context> context) {
178   return CodeStubAssembler::AllocateJSArray(
179       PACKED_ELEMENTS,
180       LoadJSArrayElementsMap(PACKED_ELEMENTS, LoadNativeContext(context)),
181       IntPtrConstant(0), SmiConstant(0));
182 }
183 
TF_BUILTIN(ListFormatPrototypeFormat,IntlBuiltinsAssembler)184 TF_BUILTIN(ListFormatPrototypeFormat, IntlBuiltinsAssembler) {
185   ListFormatCommon(
186       CAST(Parameter(Descriptor::kContext)),
187       UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)),
188       Runtime::kFormatList, "Intl.ListFormat.prototype.format");
189 }
190 
TF_BUILTIN(ListFormatPrototypeFormatToParts,IntlBuiltinsAssembler)191 TF_BUILTIN(ListFormatPrototypeFormatToParts, IntlBuiltinsAssembler) {
192   ListFormatCommon(
193       CAST(Parameter(Descriptor::kContext)),
194       UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)),
195       Runtime::kFormatListToParts, "Intl.ListFormat.prototype.formatToParts");
196 }
197 
198 }  // namespace internal
199 }  // namespace v8
200