1/*
2 * Copyright (C) 2008-2018 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 "RegExpInlines.h"
28#include "RegExpObject.h"
29
30namespace JSC {
31
32static const PropertyOffset RegExpMatchesArrayIndexPropertyOffset = 100;
33static const PropertyOffset RegExpMatchesArrayInputPropertyOffset = 101;
34static const PropertyOffset RegExpMatchesArrayGroupsPropertyOffset = 102;
35
36ALWAYS_INLINE JSArray* tryCreateUninitializedRegExpMatchesArray(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
37{
38 VM& vm = scope.vm();
39 unsigned vectorLength = initialLength;
40 if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
41 return 0;
42
43 const bool hasIndexingHeader = true;
44 Butterfly* butterfly = Butterfly::tryCreateUninitialized(vm, nullptr, 0, structure->outOfLineCapacity(), hasIndexingHeader, vectorLength * sizeof(EncodedJSValue), deferralContext);
45 if (UNLIKELY(!butterfly))
46 return nullptr;
47
48 butterfly->setVectorLength(vectorLength);
49 butterfly->setPublicLength(initialLength);
50
51 for (unsigned i = initialLength; i < vectorLength; ++i)
52 butterfly->contiguous().atUnsafe(i).clear();
53
54 JSArray* result = JSArray::createWithButterfly(vm, deferralContext, structure, butterfly);
55
56 const bool createUninitialized = true;
57 scope.notifyAllocated(result, createUninitialized);
58 return result;
59}
60
61ALWAYS_INLINE JSArray* createRegExpMatchesArray(
62 VM& vm, JSGlobalObject* globalObject, JSString* input, const String& inputValue,
63 RegExp* regExp, unsigned startOffset, MatchResult& result)
64{
65 if (validateDFGDoesGC)
66 RELEASE_ASSERT(vm.heap.expectDoesGC());
67
68 Vector<int, 32> subpatternResults;
69 int position = regExp->matchInline(vm, inputValue, startOffset, subpatternResults);
70 if (position == -1) {
71 result = MatchResult::failed();
72 return nullptr;
73 }
74
75 result.start = position;
76 result.end = subpatternResults[1];
77
78 JSArray* array;
79
80 // FIXME: This should handle array allocation errors gracefully.
81 // https://bugs.webkit.org/show_bug.cgi?id=155144
82
83 unsigned numSubpatterns = regExp->numSubpatterns();
84 bool hasNamedCaptures = regExp->hasNamedCaptures();
85 JSObject* groups = nullptr;
86 Structure* matchStructure = globalObject->regExpMatchesArrayStructure();
87 if (hasNamedCaptures) {
88 groups = JSFinalObject::create(vm, JSFinalObject::createStructure(vm, globalObject, globalObject->objectPrototype(), 0));
89 matchStructure = globalObject->regExpMatchesArrayWithGroupsStructure();
90 }
91
92 auto setProperties = [&] () {
93 array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, jsNumber(result.start));
94 array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input);
95 if (hasNamedCaptures)
96 array->putDirect(vm, RegExpMatchesArrayGroupsPropertyOffset, groups);
97
98 ASSERT(!array->butterfly()->indexingHeader()->preCapacity(matchStructure));
99 auto capacity = matchStructure->outOfLineCapacity();
100 auto size = matchStructure->outOfLineSize();
101 memset(array->butterfly()->base(0, capacity), 0, (capacity - size) * sizeof(JSValue));
102 };
103
104 if (UNLIKELY(globalObject->isHavingABadTime())) {
105 GCDeferralContext deferralContext(vm.heap);
106 ObjectInitializationScope scope(vm);
107 array = JSArray::tryCreateUninitializedRestricted(scope, &deferralContext, matchStructure, numSubpatterns + 1);
108
109 // FIXME: we should probably throw an out of memory error here, but
110 // when making this change we should check that all clients of this
111 // function will correctly handle an exception being thrown from here.
112 // https://bugs.webkit.org/show_bug.cgi?id=169786
113 RELEASE_ASSERT(array);
114
115 setProperties();
116
117 array->initializeIndexWithoutBarrier(scope, 0, jsSubstringOfResolved(vm, &deferralContext, input, result.start, result.end - result.start));
118
119 for (unsigned i = 1; i <= numSubpatterns; ++i) {
120 int start = subpatternResults[2 * i];
121 JSValue value;
122 if (start >= 0)
123 value = jsSubstringOfResolved(vm, &deferralContext, input, start, subpatternResults[2 * i + 1] - start);
124 else
125 value = jsUndefined();
126 array->initializeIndexWithoutBarrier(scope, i, value);
127 }
128 } else {
129 GCDeferralContext deferralContext(vm.heap);
130 ObjectInitializationScope scope(vm);
131 array = tryCreateUninitializedRegExpMatchesArray(scope, &deferralContext, matchStructure, numSubpatterns + 1);
132
133 // FIXME: we should probably throw an out of memory error here, but
134 // when making this change we should check that all clients of this
135 // function will correctly handle an exception being thrown from here.
136 // https://bugs.webkit.org/show_bug.cgi?id=169786
137 RELEASE_ASSERT(array);
138
139 setProperties();
140
141 array->initializeIndexWithoutBarrier(scope, 0, jsSubstringOfResolved(vm, &deferralContext, input, result.start, result.end - result.start), ArrayWithContiguous);
142
143 for (unsigned i = 1; i <= numSubpatterns; ++i) {
144 int start = subpatternResults[2 * i];
145 JSValue value;
146 if (start >= 0)
147 value = jsSubstringOfResolved(vm, &deferralContext, input, start, subpatternResults[2 * i + 1] - start);
148 else
149 value = jsUndefined();
150 array->initializeIndexWithoutBarrier(scope, i, value, ArrayWithContiguous);
151 }
152 }
153
154 // Now the object is safe to scan by GC.
155
156 // We initialize the groups object late as it could allocate, which with the current API could cause
157 // allocations.
158 if (groups) {
159 for (unsigned i = 1; i <= numSubpatterns; ++i) {
160 String groupName = regExp->getCaptureGroupName(i);
161 if (!groupName.isEmpty())
162 groups->putDirect(vm, Identifier::fromString(&vm, groupName), array->getIndexQuickly(i));
163 }
164 }
165 return array;
166}
167
168inline JSArray* createRegExpMatchesArray(ExecState* exec, JSGlobalObject* globalObject, JSString* string, RegExp* regExp, unsigned startOffset)
169{
170 MatchResult ignoredResult;
171 return createRegExpMatchesArray(globalObject->vm(), globalObject, string, string->value(exec), regExp, startOffset, ignoredResult);
172}
173JSArray* createEmptyRegExpMatchesArray(JSGlobalObject*, JSString*, RegExp*);
174Structure* createRegExpMatchesArrayStructure(VM&, JSGlobalObject*);
175Structure* createRegExpMatchesArraySlowPutStructure(VM&, JSGlobalObject*);
176Structure* createRegExpMatchesArrayWithGroupsStructure(VM&, JSGlobalObject*);
177Structure* createRegExpMatchesArrayWithGroupsSlowPutStructure(VM&, JSGlobalObject*);
178
179} // namespace JSC
180