1/*
2 * Copyright (C) 2008-2019 Apple Inc. All Rights Reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 */
19
20#pragma once
21
22#include "ButterflyInlines.h"
23#include "GCDeferralContextInlines.h"
24#include "JSArray.h"
25#include "JSCInlines.h"
26#include "JSGlobalObject.h"
27#include "ObjectConstructor.h"
28#include "RegExpInlines.h"
29#include "RegExpObject.h"
30
31namespace JSC {
32
33static const PropertyOffset RegExpMatchesArrayIndexPropertyOffset = 100;
34static const PropertyOffset RegExpMatchesArrayInputPropertyOffset = 101;
35static const PropertyOffset RegExpMatchesArrayGroupsPropertyOffset = 102;
36
37ALWAYS_INLINE JSArray* tryCreateUninitializedRegExpMatchesArray(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
38{
39 VM& vm = scope.vm();
40 unsigned vectorLength = initialLength;
41 if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
42 return 0;
43
44 const bool hasIndexingHeader = true;
45 Butterfly* butterfly = Butterfly::tryCreateUninitialized(vm, nullptr, 0, structure->outOfLineCapacity(), hasIndexingHeader, vectorLength * sizeof(EncodedJSValue), deferralContext);
46 if (UNLIKELY(!butterfly))
47 return nullptr;
48
49 butterfly->setVectorLength(vectorLength);
50 butterfly->setPublicLength(initialLength);
51
52 for (unsigned i = initialLength; i < vectorLength; ++i)
53 butterfly->contiguous().atUnsafe(i).clear();
54
55 JSArray* result = JSArray::createWithButterfly(vm, deferralContext, structure, butterfly);
56
57 const bool createUninitialized = true;
58 scope.notifyAllocated(result, createUninitialized);
59 return result;
60}
61
62ALWAYS_INLINE JSArray* createRegExpMatchesArray(
63 VM& vm, JSGlobalObject* globalObject, JSString* input, const String& inputValue,
64 RegExp* regExp, unsigned startOffset, MatchResult& result)
65{
66 if (validateDFGDoesGC)
67 RELEASE_ASSERT(vm.heap.expectDoesGC());
68
69 Vector<int, 32> subpatternResults;
70 int position = regExp->matchInline(vm, inputValue, startOffset, subpatternResults);
71 if (position == -1) {
72 result = MatchResult::failed();
73 return nullptr;
74 }
75
76 result.start = position;
77 result.end = subpatternResults[1];
78
79 JSArray* array;
80
81 // FIXME: This should handle array allocation errors gracefully.
82 // https://bugs.webkit.org/show_bug.cgi?id=155144
83
84 unsigned numSubpatterns = regExp->numSubpatterns();
85 bool hasNamedCaptures = regExp->hasNamedCaptures();
86 JSObject* groups = hasNamedCaptures ? constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure()) : nullptr;
87 Structure* matchStructure = globalObject->regExpMatchesArrayStructure();
88
89 auto setProperties = [&] () {
90 array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, jsNumber(result.start));
91 array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input);
92 array->putDirect(vm, RegExpMatchesArrayGroupsPropertyOffset, hasNamedCaptures ? groups : jsUndefined());
93
94 ASSERT(!array->butterfly()->indexingHeader()->preCapacity(matchStructure));
95 auto capacity = matchStructure->outOfLineCapacity();
96 auto size = matchStructure->outOfLineSize();
97 gcSafeZeroMemory(static_cast<JSValue*>(array->butterfly()->base(0, capacity)), (capacity - size) * sizeof(JSValue));
98 };
99
100 if (UNLIKELY(globalObject->isHavingABadTime())) {
101 GCDeferralContext deferralContext(vm.heap);
102 ObjectInitializationScope scope(vm);
103 array = JSArray::tryCreateUninitializedRestricted(scope, &deferralContext, matchStructure, numSubpatterns + 1);
104
105 // FIXME: we should probably throw an out of memory error here, but
106 // when making this change we should check that all clients of this
107 // function will correctly handle an exception being thrown from here.
108 // https://bugs.webkit.org/show_bug.cgi?id=169786
109 RELEASE_ASSERT(array);
110
111 setProperties();
112
113 array->initializeIndexWithoutBarrier(scope, 0, jsSubstringOfResolved(vm, &deferralContext, input, result.start, result.end - result.start));
114
115 for (unsigned i = 1; i <= numSubpatterns; ++i) {
116 int start = subpatternResults[2 * i];
117 JSValue value;
118 if (start >= 0)
119 value = jsSubstringOfResolved(vm, &deferralContext, input, start, subpatternResults[2 * i + 1] - start);
120 else
121 value = jsUndefined();
122 array->initializeIndexWithoutBarrier(scope, i, value);
123 }
124 } else {
125 GCDeferralContext deferralContext(vm.heap);
126 ObjectInitializationScope scope(vm);
127 array = tryCreateUninitializedRegExpMatchesArray(scope, &deferralContext, matchStructure, numSubpatterns + 1);
128
129 // FIXME: we should probably throw an out of memory error here, but
130 // when making this change we should check that all clients of this
131 // function will correctly handle an exception being thrown from here.
132 // https://bugs.webkit.org/show_bug.cgi?id=169786
133 RELEASE_ASSERT(array);
134
135 setProperties();
136
137 array->initializeIndexWithoutBarrier(scope, 0, jsSubstringOfResolved(vm, &deferralContext, input, result.start, result.end - result.start), ArrayWithContiguous);
138
139 for (unsigned i = 1; i <= numSubpatterns; ++i) {
140 int start = subpatternResults[2 * i];
141 JSValue value;
142 if (start >= 0)
143 value = jsSubstringOfResolved(vm, &deferralContext, input, start, subpatternResults[2 * i + 1] - start);
144 else
145 value = jsUndefined();
146 array->initializeIndexWithoutBarrier(scope, i, value, ArrayWithContiguous);
147 }
148 }
149
150 // Now the object is safe to scan by GC.
151
152 // We initialize the groups object late as it could allocate, which with the current API could cause
153 // allocations.
154 if (hasNamedCaptures) {
155 for (unsigned i = 1; i <= numSubpatterns; ++i) {
156 String groupName = regExp->getCaptureGroupName(i);
157 if (!groupName.isEmpty())
158 groups->putDirect(vm, Identifier::fromString(vm, groupName), array->getIndexQuickly(i));
159 }
160 }
161 return array;
162}
163
164inline JSArray* createRegExpMatchesArray(JSGlobalObject* globalObject, JSString* string, RegExp* regExp, unsigned startOffset)
165{
166 VM& vm = getVM(globalObject);
167 auto scope = DECLARE_THROW_SCOPE(vm);
168
169 MatchResult ignoredResult;
170 String input = string->value(globalObject);
171 RETURN_IF_EXCEPTION(scope, { });
172
173 RELEASE_AND_RETURN(scope, createRegExpMatchesArray(vm, globalObject, string, input, regExp, startOffset, ignoredResult));
174}
175JSArray* createEmptyRegExpMatchesArray(JSGlobalObject*, JSString*, RegExp*);
176Structure* createRegExpMatchesArrayStructure(VM&, JSGlobalObject*);
177Structure* createRegExpMatchesArraySlowPutStructure(VM&, JSGlobalObject*);
178
179} // namespace JSC
180