Commit c64a32b1 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Change interface of builtin StringSubstring

This CL changes the builtin
  StringSubstring(string, start, end)
to take start and end as untagged IntPtr values.

Bug: v8:7250, v8:7340

Change-Id: I39700d087da903f076a6ca163a8f880d31eea3a0
Reviewed-on: https://chromium-review.googlesource.com/923961
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51556}
parent c1eaae64
......@@ -101,7 +101,7 @@ namespace internal {
TFC(StringLessThan, Compare, 1) \
TFC(StringLessThanOrEqual, Compare, 1) \
TFS(StringRepeat, kString, kCount) \
TFS(StringSubstring, kString, kFrom, kTo) \
TFC(StringSubstring, StringSubstring, 1) \
\
/* OrderedHashTable helpers */ \
TFS(OrderedHashTableHealIndex, kTable, kIndex) \
......
......@@ -152,7 +152,8 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
// Calculate the substring of the first match before creating the result array
// to avoid an unnecessary write barrier storing the first result.
TNode<String> const first = SubString(string, start, end);
TNode<String> const first = SubString(string, SmiUntag(start), SmiUntag(end));
Node* const result =
AllocateRegExpResult(context, num_results, start, string);
......@@ -188,7 +189,8 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1));
Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1);
TNode<String> const capture = SubString(string, start, end);
TNode<String> const capture =
SubString(string, SmiUntag(start), SmiUntag(end));
StoreFixedArrayElement(result_elements, to_cursor, capture);
Goto(&next_iter);
......@@ -1830,7 +1832,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
Node* const match_to = LoadFixedArrayElement(
match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
var_match.Bind(SubString(string, match_from, match_to));
var_match.Bind(
SubString(string, SmiUntag(match_from), SmiUntag(match_to)));
Goto(&if_didmatch);
} else {
DCHECK(!is_fastpath);
......@@ -2248,7 +2251,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
{
Node* const from = last_matched_until;
Node* const to = match_from;
array.Push(SubString(string, from, to));
array.Push(SubString(string, SmiUntag(from), SmiUntag(to)));
GotoIf(WordEqual(array.length(), int_limit), &out);
}
......@@ -2285,7 +2288,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
BIND(&select_capture);
{
var_value.Bind(SubString(string, from, to));
var_value.Bind(SubString(string, SmiUntag(from), SmiUntag(to)));
Goto(&store_value);
}
......@@ -2320,7 +2323,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
{
Node* const from = var_last_matched_until.value();
Node* const to = string_length;
array.Push(SubString(string, from, to));
array.Push(SubString(string, SmiUntag(from), SmiUntag(to)));
Goto(&out);
}
......@@ -2690,7 +2693,8 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
// TODO(jgruber): We could skip many of the checks that using SubString
// here entails.
TNode<String> const first_part =
SubString(string, var_last_match_end.value(), match_start);
SubString(string, SmiUntag(var_last_match_end.value()),
SmiUntag(match_start));
var_result = StringAdd(context, var_result.value(), first_part);
Goto(&loop_end);
}
......@@ -2698,7 +2702,8 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
BIND(&if_replaceisnotempty);
{
TNode<String> const first_part =
SubString(string, var_last_match_end.value(), match_start);
SubString(string, SmiUntag(var_last_match_end.value()),
SmiUntag(match_start));
TNode<String> result =
StringAdd(context, var_result.value(), first_part);
var_result = StringAdd(context, result, replace_string);
......@@ -2725,8 +2730,8 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
BIND(&if_nofurthermatches);
{
TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
TNode<String> const last_part =
SubString(string, var_last_match_end.value(), string_length);
TNode<String> const last_part = SubString(
string, SmiUntag(var_last_match_end.value()), SmiUntag(string_length));
var_result = StringAdd(context, var_result.value(), last_part);
Goto(&out);
}
......
......@@ -1145,7 +1145,7 @@ compiler::Node* StringBuiltinsAssembler::GetSubstitution(
Node* const matched =
CallBuiltin(Builtins::kStringSubstring, context, subject_string,
match_start_index, match_end_index);
SmiUntag(match_start_index), SmiUntag(match_end_index));
Node* const replacement_string =
CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
match_start_index, replace_string, dollar_index);
......@@ -1383,7 +1383,7 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
GotoIf(SmiEqual(match_start_index, smi_zero), &next);
Node* const prefix =
CallBuiltin(Builtins::kStringSubstring, context, subject_string,
smi_zero, match_start_index);
IntPtrConstant(0), SmiUntag(match_start_index));
var_result.Bind(prefix);
Goto(&next);
......@@ -1424,7 +1424,7 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
{
Node* const suffix =
CallBuiltin(Builtins::kStringSubstring, context, subject_string,
match_end_index, subject_length);
SmiUntag(match_end_index), SmiUntag(subject_length));
Node* const result =
CallStub(stringadd_callable, context, var_result.value(), suffix);
Return(result);
......@@ -1605,7 +1605,7 @@ class StringPadAssembler : public StringBuiltinsAssembler {
{
Node* const remainder_string = CallBuiltin(
Builtins::kStringSubstring, context, var_fill_string.value(),
SmiConstant(0), SmiFromInt32(remaining_word32));
IntPtrConstant(0), ChangeInt32ToIntPtr(remaining_word32));
var_pad.Bind(CallStub(stringadd_callable, context, var_pad.value(),
remainder_string));
Goto(&return_result);
......@@ -1649,8 +1649,8 @@ TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
// ES6 section 21.1.3.18 String.prototype.slice ( start, end )
TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
Label out(this);
TVARIABLE(Smi, var_start);
TVARIABLE(Smi, var_end);
TVARIABLE(IntPtrT, var_start);
TVARIABLE(IntPtrT, var_end);
const int kStart = 0;
const int kEnd = 1;
......@@ -1670,7 +1670,7 @@ TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
CAST(CallBuiltin(Builtins::kToString, context, receiver));
// 3. Let len be the number of elements in S.
TNode<Smi> const length = LoadStringLengthAsSmi(subject_string);
TNode<IntPtrT> const length = LoadStringLengthAsWord(subject_string);
// Convert {start} to a relative index.
var_start = ConvertToRelativeIndex(context, start, length);
......@@ -1686,7 +1686,7 @@ TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
Label return_emptystring(this);
BIND(&out);
{
GotoIf(SmiLessThanOrEqual(var_end.value(), var_start.value()),
GotoIf(IntPtrLessThanOrEqual(var_end.value(), var_start.value()),
&return_emptystring);
TNode<String> const result =
SubString(subject_string, var_start.value(), var_end.value());
......@@ -1813,16 +1813,16 @@ TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
Label out(this);
TVARIABLE(Smi, var_start);
TVARIABLE(IntPtrT, var_start);
TVARIABLE(Number, var_length);
TNode<Smi> const zero = SmiConstant(0);
TNode<IntPtrT> const zero = IntPtrConstant(0);
// Check that {receiver} is coercible to Object and convert it to a String.
TNode<String> const string =
ToThisString(context, receiver, "String.prototype.substr");
TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
// Convert {start} to a relative index.
var_start = ConvertToRelativeIndex(context, start, string_length);
......@@ -1836,7 +1836,7 @@ TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
Branch(IsUndefined(length), &if_isundefined, &if_isnotundefined);
BIND(&if_isundefined);
var_length = string_length;
var_length = SmiTag(string_length);
Goto(&if_issmi);
BIND(&if_isnotundefined);
......@@ -1844,18 +1844,20 @@ TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
CodeStubAssembler::kTruncateMinusZero);
}
TVARIABLE(Smi, var_result_length);
TVARIABLE(IntPtrT, var_result_length);
Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
// Set {length} to min(max({length}, 0), {string_length} - {start}
BIND(&if_issmi);
{
TNode<Smi> const positive_length = SmiMax(CAST(var_length.value()), zero);
TNode<Smi> const minimal_length = SmiSub(string_length, var_start.value());
var_result_length = SmiMin(positive_length, minimal_length);
TNode<IntPtrT> const positive_length =
IntPtrMax(SmiUntag(CAST(var_length.value())), zero);
TNode<IntPtrT> const minimal_length =
IntPtrSub(string_length, var_start.value());
var_result_length = IntPtrMin(positive_length, minimal_length);
GotoIfNot(SmiLessThanOrEqual(var_result_length.value(), zero), &out);
GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
args.PopAndReturn(EmptyStringConstant());
}
......@@ -1879,15 +1881,16 @@ TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
BIND(&if_ispositive);
{
var_result_length = SmiSub(string_length, var_start.value());
GotoIfNot(SmiLessThanOrEqual(var_result_length.value(), zero), &out);
var_result_length = IntPtrSub(string_length, var_start.value());
GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
args.PopAndReturn(EmptyStringConstant());
}
}
BIND(&out);
{
TNode<Smi> const end = SmiAdd(var_start.value(), var_result_length.value());
TNode<IntPtrT> const end =
IntPtrAdd(var_start.value(), var_result_length.value());
args.PopAndReturn(SubString(string, var_start.value(), end));
}
}
......@@ -1898,7 +1901,7 @@ TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
Label out(this);
TVARIABLE(Smi, var_result);
TNode<Object> const value_int =
TNode<Number> const value_int =
ToInteger_Inline(context, value, CodeStubAssembler::kTruncateMinusZero);
Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
......@@ -1906,8 +1909,9 @@ TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
BIND(&if_issmi);
{
TNode<Smi> value_smi = CAST(value_int);
Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
Branch(SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds);
Branch(SmiAbove(value_smi, limit), &if_isoutofbounds, &if_isinbounds);
BIND(&if_isinbounds);
{
......@@ -1919,7 +1923,7 @@ TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
{
TNode<Smi> const zero = SmiConstant(0);
var_result =
SelectTaggedConstant(SmiLessThan(value_int, zero), zero, limit);
SelectTaggedConstant(SmiLessThan(value_smi, zero), zero, limit);
Goto(&out);
}
}
......@@ -1943,8 +1947,8 @@ TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
TF_BUILTIN(StringSubstring, CodeStubAssembler) {
TNode<String> string = CAST(Parameter(Descriptor::kString));
Node* from = Parameter(Descriptor::kFrom);
Node* to = Parameter(Descriptor::kTo);
TNode<IntPtrT> from = UncheckedCast<IntPtrT>(Parameter(Descriptor::kFrom));
TNode<IntPtrT> to = UncheckedCast<IntPtrT>(Parameter(Descriptor::kTo));
Return(SubString(string, from, to));
}
......@@ -1998,7 +2002,10 @@ TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
}
BIND(&out);
{ args.PopAndReturn(SubString(string, var_start.value(), var_end.value())); }
{
args.PopAndReturn(SubString(string, SmiUntag(var_start.value()),
SmiUntag(var_end.value())));
}
}
// ES6 #sec-string.prototype.trim
......@@ -2051,8 +2058,8 @@ void StringTrimAssembler::Generate(String::TrimMode mode,
}
arguments.PopAndReturn(
SubString(string, SmiTag(var_start.value()),
SmiAdd(SmiTag(var_end.value()), SmiConstant(1))));
SubString(string, var_start.value(),
IntPtrAdd(var_end.value(), IntPtrConstant(1))));
BIND(&if_runtime);
arguments.PopAndReturn(
......
......@@ -147,10 +147,12 @@ TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
TNode<Map> fixed_typed_map = LoadMapForType(holder);
GotoIf(TaggedIsNotSmi(byte_length), &allocate_off_heap);
GotoIf(
SmiGreaterThan(byte_length, SmiConstant(V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP)),
&allocate_off_heap);
TNode<IntPtrT> word_byte_length = SmiToIntPtr(CAST(byte_length));
// The goto above ensures that byte_length is a Smi.
TNode<Smi> smi_byte_length = CAST(byte_length);
GotoIf(SmiGreaterThan(smi_byte_length,
SmiConstant(V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP)),
&allocate_off_heap);
TNode<IntPtrT> word_byte_length = SmiToIntPtr(smi_byte_length);
Goto(&allocate_on_heap);
BIND(&allocate_on_heap);
......@@ -305,7 +307,9 @@ void TypedArrayBuiltinsAssembler::ConstructByLength(TNode<Context> context,
// Note: this is not per spec, but rather a constraint of our current
// representation (which uses Smis).
GotoIf(TaggedIsNotSmi(converted_length), &invalid_length);
GotoIf(SmiLessThan(converted_length, SmiConstant(0)), &invalid_length);
// The goto above ensures that byte_length is a Smi.
TNode<Smi> smi_converted_length = CAST(converted_length);
GotoIf(SmiLessThan(smi_converted_length, SmiConstant(0)), &invalid_length);
Node* initialize = TrueConstant();
CallBuiltin(Builtins::kTypedArrayInitialize, context, holder,
......@@ -1245,16 +1249,19 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
// Convert start offset argument to integer, and calculate relative offset.
TNode<Object> start = args.GetOptionalArgumentValue(0, SmiConstant(0));
TNode<Smi> start_index =
ConvertToRelativeIndex(context, start, source_length);
SmiTag(ConvertToRelativeIndex(context, start, SmiUntag(source_length)));
// Convert end offset argument to integer, and calculate relative offset.
// If end offset is not given or undefined is given, set source_length to
// "end_index".
TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
TNode<Smi> end_index = Select<Smi>(
IsUndefined(end), [=] { return source_length; },
[=] { return ConvertToRelativeIndex(context, end, source_length); },
MachineRepresentation::kTagged);
TNode<Smi> end_index =
Select<Smi>(IsUndefined(end), [=] { return source_length; },
[=] {
return SmiTag(ConvertToRelativeIndex(
context, end, SmiUntag(source_length)));
},
MachineRepresentation::kTagged);
// Create a result array by invoking TypedArraySpeciesCreate.
TNode<Smi> count = SmiMax(SmiSub(end_index, start_index), SmiConstant(0));
......@@ -1367,7 +1374,8 @@ TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
// 8. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin),
// 0); else let beginIndex be min(relativeBegin, srcLength).
TNode<Object> begin = args.GetOptionalArgumentValue(0, SmiConstant(0));
var_begin = ConvertToRelativeIndex(context, begin, source_length);
var_begin =
SmiTag(ConvertToRelativeIndex(context, begin, SmiUntag(source_length)));
TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
// 9. If end is undefined, let relativeEnd be srcLength;
......@@ -1377,7 +1385,8 @@ TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
// else, let relativeEnd be ? ToInteger(end).
// 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0);
// else let endIndex be min(relativeEnd, srcLength).
var_end = ConvertToRelativeIndex(context, end, source_length);
var_end =
SmiTag(ConvertToRelativeIndex(context, end, SmiUntag(source_length)));
Goto(&offset_done);
BIND(&offset_done);
......
This diff is collapsed.
......@@ -220,8 +220,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// After converting an index to an integer, calculate a relative index: if
// index < 0, max(length + index, 0); else min(index, length)
TNode<Smi> ConvertToRelativeIndex(TNode<Context> context, TNode<Object> index,
TNode<Smi> length);
TNode<IntPtrT> ConvertToRelativeIndex(TNode<Context> context,
TNode<Object> index,
TNode<IntPtrT> length);
// Tag an IntPtr as a Smi value.
TNode<Smi> SmiTag(SloppyTNode<IntPtrT> value);
......@@ -773,12 +774,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Allocate a SlicedOneByteString with the given length, parent and offset.
// |length| and |offset| are expected to be tagged.
TNode<String> AllocateSlicedOneByteString(TNode<Smi> length, Node* parent,
Node* offset);
TNode<String> AllocateSlicedOneByteString(TNode<Smi> length,
TNode<String> parent,
TNode<Smi> offset);
// Allocate a SlicedTwoByteString with the given length, parent and offset.
// |length| and |offset| are expected to be tagged.
TNode<String> AllocateSlicedTwoByteString(TNode<Smi> length, Node* parent,
Node* offset);
TNode<String> AllocateSlicedTwoByteString(TNode<Smi> length,
TNode<String> parent,
TNode<Smi> offset);
// Allocate a one-byte ConsString with the given length, first and second
// parts. |length| is expected to be tagged, and |first| and |second| are
......@@ -1219,8 +1223,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Return a new string object which holds a substring containing the range
// [from,to[ of string. |from| and |to| are expected to be tagged.
TNode<String> SubString(TNode<String> string, SloppyTNode<Smi> from,
SloppyTNode<Smi> to);
TNode<String> SubString(TNode<String> string, TNode<IntPtrT> from,
TNode<IntPtrT> to);
// Return a new string object produced by concatenating |first| with |second|.
TNode<String> StringAdd(Node* context, TNode<String> first,
......@@ -2026,8 +2030,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Label* bailout);
TNode<String> AllocateSlicedString(Heap::RootListIndex map_root_index,
TNode<Smi> length, Node* parent,
Node* offset);
TNode<Smi> length, TNode<String> parent,
TNode<Smi> offset);
TNode<String> AllocateConsString(Heap::RootListIndex map_root_index,
TNode<Smi> length, TNode<String> first,
......@@ -2161,7 +2165,7 @@ class ToDirectStringAssembler : public CodeStubAssembler {
// string. The result can be either a sequential or external string.
// Jumps to if_bailout if the string if the string is indirect and cannot
// be unpacked.
Node* TryToDirect(Label* if_bailout);
TNode<String> TryToDirect(Label* if_bailout);
// Returns a pointer to the beginning of the string data.
// Jumps to if_bailout if the external string cannot be unpacked.
......@@ -2177,7 +2181,9 @@ class ToDirectStringAssembler : public CodeStubAssembler {
Node* string() { return var_string_.value(); }
Node* instance_type() { return var_instance_type_.value(); }
Node* offset() { return var_offset_.value(); }
TNode<IntPtrT> offset() {
return UncheckedCast<IntPtrT>(var_offset_.value());
}
Node* is_external() { return var_is_external_.value(); }
private:
......
......@@ -3196,8 +3196,8 @@ Node* EffectControlLinearizer::LowerStringComparison(Callable const& callable,
Node* EffectControlLinearizer::LowerStringSubstring(Node* node) {
Node* receiver = node->InputAt(0);
Node* start = node->InputAt(1);
Node* end = node->InputAt(2);
Node* start = ChangeInt32ToIntPtr(node->InputAt(1));
Node* end = ChangeInt32ToIntPtr(node->InputAt(2));
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kStringSubstring);
......
......@@ -2410,8 +2410,8 @@ class RepresentationSelector {
}
case IrOpcode::kStringSubstring: {
ProcessInput(node, 0, UseInfo::AnyTagged());
ProcessInput(node, 1, UseInfo::TaggedSigned());
ProcessInput(node, 2, UseInfo::TaggedSigned());
ProcessInput(node, 1, UseInfo::TruncatingWord32());
ProcessInput(node, 2, UseInfo::TruncatingWord32());
SetOutput(node, MachineRepresentation::kTaggedPointer);
return;
}
......
......@@ -284,6 +284,21 @@ void StringAtDescriptor::InitializePlatformSpecific(
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void StringSubstringDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) {
// kString, kFrom, kTo
// TODO(turbofan): Allow builtins to return untagged values.
MachineType machine_types[] = {MachineType::AnyTagged(),
MachineType::IntPtr(), MachineType::IntPtr()};
data->InitializePlatformIndependent(arraysize(machine_types), 0,
machine_types);
}
void StringSubstringDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void TypeConversionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {ArgumentRegister()};
......
......@@ -62,6 +62,7 @@ class PlatformInterfaceDescriptor;
V(BinaryOp) \
V(StringAdd) \
V(StringAt) \
V(StringSubstring) \
V(ForInPrepare) \
V(GetProperty) \
V(ArgumentAdaptor) \
......@@ -764,6 +765,13 @@ class StringAtDescriptor final : public CallInterfaceDescriptor {
CallInterfaceDescriptor)
};
class StringSubstringDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kString, kFrom, kTo)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(StringSubstringDescriptor,
CallInterfaceDescriptor)
};
class ArgumentAdaptorDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kFunction, kNewTarget, kActualArgumentsCount,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment