2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
26 #include "CallFrame.h"
27 #include "CommonIdentifiers.h"
28 #include "Identifier.h"
29 #include "PropertyDescriptor.h"
30 #include "PropertySlot.h"
32 #include "Structure.h"
38 JSString* jsEmptyString(JSGlobalData*);
39 JSString* jsEmptyString(ExecState*);
40 JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string
41 JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string
43 JSString* jsSingleCharacterString(JSGlobalData*, UChar);
44 JSString* jsSingleCharacterString(ExecState*, UChar);
45 JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
46 JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
47 JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);
49 // Non-trivial strings are two or more characters long.
50 // These functions are faster than just calling jsString.
51 JSString* jsNontrivialString(JSGlobalData*, const UString&);
52 JSString* jsNontrivialString(ExecState*, const UString&);
53 JSString* jsNontrivialString(JSGlobalData*, const char*);
54 JSString* jsNontrivialString(ExecState*, const char*);
56 // Should be used for strings that are owned by an object that will
57 // likely outlive the JSValue this makes, such as the parse tree or a
58 // DOM object that contains a UString
59 JSString* jsOwnedString(JSGlobalData*, const UString&);
60 JSString* jsOwnedString(ExecState*, const UString&);
62 class JS_EXPORTCLASS JSString : public JSCell {
66 friend class JSGlobalData;
68 friend class SpecializedThunkJIT;
69 friend struct ThunkHelpers;
75 RopeBuilder(unsigned fiberCount)
77 , m_rope(RopeImpl::tryCreateUninitialized(fiberCount))
81 bool isOutOfMemory() { return !m_rope; }
83 void append(RopeImpl::Fiber& fiber)
86 m_rope->initializeFiber(m_index, fiber);
88 void append(const UString& string)
91 m_rope->initializeFiber(m_index, string.impl());
93 void append(JSString* jsString)
95 if (jsString->isRope()) {
96 for (unsigned i = 0; i < jsString->m_fiberCount; ++i)
97 append(jsString->m_fibers[i]);
99 append(jsString->string());
102 PassRefPtr<RopeImpl> release()
104 ASSERT(m_index == m_rope->fiberCount());
105 return m_rope.release();
108 unsigned length() { return m_rope->length(); }
112 RefPtr<RopeImpl> m_rope;
119 RopeIterator(RopeImpl::Fiber* fibers, size_t fiberCount)
122 m_workQueue.append(WorkItem(fibers, fiberCount));
126 RopeIterator& operator++()
128 WorkItem& item = m_workQueue.last();
129 ASSERT(!RopeImpl::isRope(item.fibers[item.i]));
130 if (++item.i == item.fiberCount)
131 m_workQueue.removeLast();
136 StringImpl* operator*()
138 WorkItem& item = m_workQueue.last();
139 RopeImpl::Fiber fiber = item.fibers[item.i];
140 ASSERT(!RopeImpl::isRope(fiber));
141 return static_cast<StringImpl*>(fiber);
144 bool operator!=(const RopeIterator& other) const
146 return m_workQueue != other.m_workQueue;
151 WorkItem(RopeImpl::Fiber* fibers, size_t fiberCount)
153 , fiberCount(fiberCount)
158 bool operator!=(const WorkItem& other) const
160 return fibers != other.fibers || fiberCount != other.fiberCount || i != other.i;
163 RopeImpl::Fiber* fibers;
170 if (m_workQueue.isEmpty())
174 WorkItem& item = m_workQueue.last();
175 RopeImpl::Fiber fiber = item.fibers[item.i];
176 if (!RopeImpl::isRope(fiber))
178 RopeImpl* rope = static_cast<RopeImpl*>(fiber);
179 if (++item.i == item.fiberCount)
180 m_workQueue.removeLast();
181 m_workQueue.append(WorkItem(rope->fibers(), rope->fiberCount()));
185 Vector<WorkItem, 16> m_workQueue;
189 ALWAYS_INLINE JSString(JSGlobalData& globalData, const UString& value)
190 : JSCell(globalData, globalData.stringStructure.get())
191 , m_length(value.length())
197 enum HasOtherOwnerType { HasOtherOwner };
198 JSString(JSGlobalData& globalData, const UString& value, HasOtherOwnerType)
199 : JSCell(globalData, globalData.stringStructure.get())
200 , m_length(value.length())
205 JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value, HasOtherOwnerType)
206 : JSCell(globalData, globalData.stringStructure.get())
207 , m_length(value->length())
212 JSString(JSGlobalData& globalData, PassRefPtr<RopeImpl> rope)
213 : JSCell(globalData, globalData.stringStructure.get())
214 , m_length(rope->length())
218 // This constructor constructs a new string by concatenating s1 & s2.
219 // This should only be called with fiberCount <= 3.
220 JSString(JSGlobalData& globalData, unsigned fiberCount, JSString* s1, JSString* s2)
221 : JSCell(globalData, globalData.stringStructure.get())
222 , m_length(s1->length() + s2->length())
223 , m_fiberCount(fiberCount)
226 // This constructor constructs a new string by concatenating s1 & s2.
227 // This should only be called with fiberCount <= 3.
228 JSString(JSGlobalData& globalData, unsigned fiberCount, JSString* s1, const UString& u2)
229 : JSCell(globalData, globalData.stringStructure.get())
230 , m_length(s1->length() + u2.length())
231 , m_fiberCount(fiberCount)
234 // This constructor constructs a new string by concatenating s1 & s2.
235 // This should only be called with fiberCount <= 3.
236 JSString(JSGlobalData& globalData, unsigned fiberCount, const UString& u1, JSString* s2)
237 : JSCell(globalData, globalData.stringStructure.get())
238 , m_length(u1.length() + s2->length())
239 , m_fiberCount(fiberCount)
242 JSString(ExecState* exec)
243 : JSCell(exec->globalData(), exec->globalData().stringStructure.get())
245 , m_fiberCount(s_maxInternalRopeLength)
249 // This constructor constructs a new string by concatenating u1 & u2.
250 JSString(JSGlobalData& globalData, const UString& u1, const UString& u2)
251 : JSCell(globalData, globalData.stringStructure.get())
252 , m_length(u1.length() + u2.length())
257 // This constructor constructs a new string by concatenating u1, u2 & u3.
258 JSString(JSGlobalData& globalData, const UString& u1, const UString& u2, const UString& u3)
259 : JSCell(globalData, globalData.stringStructure.get())
260 , m_length(u1.length() + u2.length() + u3.length())
261 , m_fiberCount(s_maxInternalRopeLength)
265 void finishCreation(JSGlobalData& globalData, const UString& value)
267 Base::finishCreation(globalData);
268 ASSERT(!m_value.isNull());
269 Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost());
272 void finishCreation(JSGlobalData& globalData)
274 Base::finishCreation(globalData);
275 ASSERT(!m_value.isNull());
278 void finishCreation(JSGlobalData& globalData, PassRefPtr<RopeImpl> rope)
280 Base::finishCreation(globalData);
281 m_fibers[0] = rope.leakRef();
284 void finishCreation(JSGlobalData& globalData, unsigned fiberCount, JSString* s1, JSString* s2)
286 Base::finishCreation(globalData);
287 ASSERT_UNUSED(fiberCount, fiberCount <= s_maxInternalRopeLength);
289 appendStringInCreate(index, s1);
290 appendStringInCreate(index, s2);
291 ASSERT(fiberCount == index);
294 void finishCreation(JSGlobalData& globalData, unsigned fiberCount, JSString* s1, const UString& u2)
296 Base::finishCreation(globalData);
297 ASSERT_UNUSED(fiberCount, fiberCount <= s_maxInternalRopeLength);
299 appendStringInCreate(index, s1);
300 appendStringInCreate(index, u2);
301 ASSERT(fiberCount == index);
304 void finishCreation(JSGlobalData& globalData, unsigned fiberCount, const UString& u1, JSString* s2)
306 Base::finishCreation(globalData);
307 ASSERT_UNUSED(fiberCount, fiberCount <= s_maxInternalRopeLength);
309 appendStringInCreate(index, u1);
310 appendStringInCreate(index, s2);
311 ASSERT(fiberCount == index);
314 // Fills in the new string by concatenating v1, v2 & v3.
315 // This should only be called with fiberCount <= 3 ... which since every
316 // value must require a fiberCount of at least one implies that the length
317 // for each value must be exactly 1!
318 void finishCreation(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
320 Base::finishCreation(exec->globalData());
322 appendValueInCreateAndIncrementLength(exec, index, v1);
323 appendValueInCreateAndIncrementLength(exec, index, v2);
324 appendValueInCreateAndIncrementLength(exec, index, v3);
325 ASSERT(index == s_maxInternalRopeLength);
328 void finishCreation(JSGlobalData& globalData, const UString& u1, const UString& u2)
330 Base::finishCreation(globalData);
332 appendStringInCreate(index, u1);
333 appendStringInCreate(index, u2);
334 ASSERT(index <= s_maxInternalRopeLength);
337 void finishCreation(JSGlobalData& globalData, const UString& u1, const UString& u2, const UString& u3)
339 Base::finishCreation(globalData);
341 appendStringInCreate(index, u1);
342 appendStringInCreate(index, u2);
343 appendStringInCreate(index, u3);
344 ASSERT(index <= s_maxInternalRopeLength);
348 static JSString* create(JSGlobalData& globalData, const UString& value)
350 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
351 newString->finishCreation(globalData, value);
354 static JSString* createHasOtherOwner(JSGlobalData& globalData, const UString& value)
356 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, value, HasOtherOwner);
357 newString->finishCreation(globalData, value);
360 static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
362 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, value, HasOtherOwner);
363 newString->finishCreation(globalData);
366 static JSString* create(JSGlobalData& globalData, PassRefPtr<RopeImpl> rope)
368 RefPtr<RopeImpl> tempRope = rope;
369 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, tempRope);
370 newString->finishCreation(globalData, tempRope);
373 static JSString* create(JSGlobalData& globalData, unsigned fiberCount, JSString* s1, JSString* s2)
375 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, fiberCount, s1, s2);
376 newString->finishCreation(globalData, fiberCount, s1, s2);
379 static JSString* create(JSGlobalData& globalData, unsigned fiberCount, JSString* s1, const UString& u2)
381 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, fiberCount, s1, u2);
382 newString->finishCreation(globalData, fiberCount, s1, u2);
385 static JSString* create(JSGlobalData& globalData, unsigned fiberCount, const UString& u1, JSString* s2)
387 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, fiberCount, u1, s2);
388 newString->finishCreation(globalData, fiberCount, u1, s2);
391 static JSString* create(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
393 JSString* newString = new (allocateCell<JSString>(*exec->heap())) JSString(exec);
394 newString->finishCreation(exec, v1, v2, v3);
397 static JSString* create(JSGlobalData& globalData, const UString& u1, const UString& u2)
399 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, u1, u2);
400 newString->finishCreation(globalData, u1, u2);
403 static JSString* create(JSGlobalData& globalData, const UString& u1, const UString& u2, const UString& u3)
405 JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, u1, u2, u3);
406 newString->finishCreation(globalData, u1, u2, u3);
412 ASSERT(vptr() == JSGlobalData::jsStringVPtr);
413 for (unsigned i = 0; i < m_fiberCount; ++i)
414 RopeImpl::deref(m_fibers[i]);
417 const UString& value(ExecState* exec) const
423 const UString& tryGetValue() const
429 unsigned length() { return m_length; }
431 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
432 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
433 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
435 bool canGetIndex(unsigned i) { return i < m_length; }
436 JSString* getIndex(ExecState*, unsigned);
437 JSString* getIndexSlowCase(ExecState*, unsigned);
439 JSValue replaceCharacter(ExecState*, UChar, const UString& replacement);
441 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
443 return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot), &s_info);
446 static const ClassInfo s_info;
449 JSString(VPtrStealingHackType)
450 : JSCell(VPtrStealingHack)
455 void resolveRope(ExecState*) const;
456 void resolveRopeSlowCase(ExecState*, UChar*) const;
457 void outOfMemory(ExecState*) const;
458 JSString* substringFromRope(ExecState*, unsigned offset, unsigned length);
460 void appendStringInCreate(unsigned& index, const UString& string)
462 StringImpl* impl = string.impl();
464 m_fibers[index++] = impl;
465 Heap::heap(this)->reportExtraMemoryCost(string.impl()->cost());
468 void appendStringInCreate(unsigned& index, JSString* jsString)
470 if (jsString->isRope()) {
471 for (unsigned i = 0; i < jsString->m_fiberCount; ++i) {
472 RopeImpl::Fiber fiber = jsString->m_fibers[i];
474 m_fibers[index++] = fiber;
477 appendStringInCreate(index, jsString->string());
480 void appendValueInCreateAndIncrementLength(ExecState* exec, unsigned& index, JSValue v)
483 ASSERT(v.asCell()->isString());
484 JSString* s = static_cast<JSString*>(v.asCell());
485 ASSERT(s->fiberCount() == 1);
486 appendStringInCreate(index, s);
487 m_length += s->length();
489 UString u(v.toString(exec));
490 StringImpl* impl = u.impl();
492 m_fibers[index++] = impl;
493 m_length += u.length();
497 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
498 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
499 bool toBoolean(ExecState*) const;
500 virtual double toNumber(ExecState*) const;
501 virtual JSObject* toObject(ExecState*, JSGlobalObject*) const;
502 virtual UString toString(ExecState*) const;
504 virtual JSObject* toThisObject(ExecState*) const;
506 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
507 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
508 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
509 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
511 static const unsigned s_maxInternalRopeLength = 3;
513 // A string is represented either by a UString or a RopeImpl.
515 mutable UString m_value;
516 mutable unsigned m_fiberCount;
517 mutable FixedArray<RopeImpl::Fiber, s_maxInternalRopeLength> m_fibers;
519 bool isRope() const { return m_fiberCount; }
520 UString& string() { ASSERT(!isRope()); return m_value; }
521 unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; }
523 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
524 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
525 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
526 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
527 friend JSValue jsString(ExecState* exec, JSValue thisValue);
528 friend JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length);
531 JSString* asString(JSValue);
533 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
534 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
535 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
536 // The below function must be called by any inline function that invokes a JSString constructor.
537 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
538 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
540 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
543 inline JSString* asString(JSValue value)
545 ASSERT(value.asCell()->isString());
546 return static_cast<JSString*>(value.asCell());
549 inline JSString* jsEmptyString(JSGlobalData* globalData)
551 return globalData->smallStrings.emptyString(globalData);
554 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
556 if (c <= maxSingleCharacterString)
557 return globalData->smallStrings.singleCharacterString(globalData, c);
558 return fixupVPtr(globalData, JSString::create(*globalData, UString(&c, 1)));
561 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
563 JSGlobalData* globalData = &exec->globalData();
564 ASSERT(offset < static_cast<unsigned>(s.length()));
566 if (c <= maxSingleCharacterString)
567 return globalData->smallStrings.singleCharacterString(globalData, c);
568 return fixupVPtr(globalData, JSString::create(*globalData, UString(StringImpl::create(s.impl(), offset, 1))));
571 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
576 return fixupVPtr(globalData, JSString::create(*globalData, s));
579 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
581 ASSERT(s.length() > 1);
582 return fixupVPtr(globalData, JSString::create(*globalData, s));
585 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
587 ASSERT(canGetIndex(i));
589 return getIndexSlowCase(exec, i);
590 ASSERT(i < m_value.length());
591 return jsSingleCharacterSubstring(exec, m_value, i);
594 inline JSString* jsString(JSGlobalData* globalData, const UString& s)
596 int size = s.length();
598 return globalData->smallStrings.emptyString(globalData);
601 if (c <= maxSingleCharacterString)
602 return globalData->smallStrings.singleCharacterString(globalData, c);
604 return fixupVPtr(globalData, JSString::create(*globalData, s));
607 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
609 ASSERT(offset <= static_cast<unsigned>(s->length()));
610 ASSERT(length <= static_cast<unsigned>(s->length()));
611 ASSERT(offset + length <= static_cast<unsigned>(s->length()));
612 JSGlobalData* globalData = &exec->globalData();
614 return globalData->smallStrings.emptyString(globalData);
616 return s->substringFromRope(exec, offset, length);
617 return jsSubstring(globalData, s->m_value, offset, length);
620 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
622 ASSERT(offset <= static_cast<unsigned>(s.length()));
623 ASSERT(length <= static_cast<unsigned>(s.length()));
624 ASSERT(offset + length <= static_cast<unsigned>(s.length()));
626 return globalData->smallStrings.emptyString(globalData);
629 if (c <= maxSingleCharacterString)
630 return globalData->smallStrings.singleCharacterString(globalData, c);
632 return fixupVPtr(globalData, JSString::createHasOtherOwner(*globalData, UString(StringImpl::create(s.impl(), offset, length))));
635 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
637 int size = s.length();
639 return globalData->smallStrings.emptyString(globalData);
642 if (c <= maxSingleCharacterString)
643 return globalData->smallStrings.singleCharacterString(globalData, c);
645 return fixupVPtr(globalData, JSString::createHasOtherOwner(*globalData, s));
648 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
649 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
650 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
651 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
652 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
653 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
654 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); }
656 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
658 if (propertyName == exec->propertyNames().length) {
659 slot.setValue(jsNumber(m_length));
664 unsigned i = propertyName.toUInt32(isStrictUInt32);
665 if (isStrictUInt32 && i < m_length) {
666 slot.setValue(getIndex(exec, i));
673 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
675 if (propertyName < m_length) {
676 slot.setValue(getIndex(exec, propertyName));
683 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
685 inline bool JSCell::toBoolean(ExecState* exec) const
688 return static_cast<const JSString*>(this)->toBoolean(exec);
689 return !structure()->typeInfo().masqueradesAsUndefined();
692 // --- JSValue inlines ----------------------------
694 inline bool JSValue::toBoolean(ExecState* exec) const
699 return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
701 return asCell()->toBoolean(exec);
702 return isTrue(); // false, null, and undefined all convert to false.
705 inline UString JSValue::toString(ExecState* exec) const
708 return static_cast<JSString*>(asCell())->value(exec);
710 return exec->globalData().numericStrings.add(asInt32());
712 return exec->globalData().numericStrings.add(asDouble());
722 return asCell()->toString(exec);
725 inline UString JSValue::toPrimitiveString(ExecState* exec) const
729 return exec->globalData().numericStrings.add(asInt32());
731 return exec->globalData().numericStrings.add(asDouble());
741 return asCell()->toPrimitive(exec, NoPreference).toString(exec);