mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-30 05:31:10 +00:00
!5399 speed reduce and sort
Merge pull request !5399 from wangyue/arraysort
This commit is contained in:
commit
ec2596517e
@ -1624,6 +1624,37 @@ JSTaggedValue BuiltinsArray::Push(EcmaRuntimeCallInfo *argv)
|
||||
return GetTaggedDouble(len);
|
||||
}
|
||||
|
||||
JSTaggedValue BuiltinsArray::ReduceUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisHandle,
|
||||
JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, JSMutableHandle<JSTaggedValue> &accumulator,
|
||||
JSHandle<JSTaggedValue> &callbackFnHandle)
|
||||
{
|
||||
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
||||
JSTaggedValue callResult = JSTaggedValue::Undefined();
|
||||
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
||||
while (k < len) {
|
||||
bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
if (exists) {
|
||||
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
key.Update(JSTaggedValue(k));
|
||||
JSHandle<JSTaggedValue> thisArgHandle = globalConst->GetHandledUndefined();
|
||||
const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
|
||||
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
||||
EcmaRuntimeCallInfo *info =
|
||||
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), key.GetTaggedValue(),
|
||||
thisObjVal.GetTaggedValue());
|
||||
callResult = JSFunction::Call(info);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
accumulator.Update(callResult);
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return accumulator.GetTaggedValue();
|
||||
}
|
||||
|
||||
// 22.1.3.18 Array.prototype.reduce ( callbackfn [ , initialValue ] )
|
||||
JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv)
|
||||
{
|
||||
@ -1631,7 +1662,6 @@ JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv)
|
||||
BUILTINS_API_TRACE(argv->GetThread(), Array, Reduce);
|
||||
JSThread *thread = argv->GetThread();
|
||||
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
||||
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
||||
|
||||
uint32_t argc = argv->GetArgsNumber();
|
||||
// 1. Let O be ToObject(this value).
|
||||
@ -1694,43 +1724,7 @@ JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv)
|
||||
if (thisObjVal->IsStableJSArray(thread)) {
|
||||
JSStableArray::Reduce(thread, thisObjHandle, callbackFnHandle, accumulator, k, len);
|
||||
}
|
||||
|
||||
// 10. Repeat, while k < len
|
||||
// a. Let Pk be ToString(k).
|
||||
// b. Let kPresent be HasProperty(O, Pk).
|
||||
// c. ReturnIfAbrupt(kPresent).
|
||||
// d. If kPresent is true, then
|
||||
// i. Let kValue be Get(O, Pk).
|
||||
// ii. ReturnIfAbrupt(kValue).
|
||||
// iii. Let accumulator be Call(callbackfn, undefined, «accumulator, kValue, k, O»).
|
||||
// iv. ReturnIfAbrupt(accumulator).
|
||||
// e. Increase k by 1.
|
||||
JSTaggedValue callResult = JSTaggedValue::Undefined();
|
||||
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
||||
while (k < len) {
|
||||
bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
if (exists) {
|
||||
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
key.Update(JSTaggedValue(k));
|
||||
JSHandle<JSTaggedValue> thisArgHandle = globalConst->GetHandledUndefined();
|
||||
const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
|
||||
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
||||
EcmaRuntimeCallInfo *info =
|
||||
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), key.GetTaggedValue(),
|
||||
thisObjVal.GetTaggedValue());
|
||||
callResult = JSFunction::Call(info);
|
||||
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
||||
accumulator.Update(callResult);
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
// 11. Return accumulator.
|
||||
return accumulator.GetTaggedValue();
|
||||
return ReduceUnStableJSArray(thread, thisHandle, thisObjVal, k, len, accumulator, callbackFnHandle);
|
||||
}
|
||||
|
||||
// 22.1.3.19 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
|
||||
|
@ -79,7 +79,7 @@
|
||||
/* Array.prototype.push ( ...items ) */ \
|
||||
V("push", Push, 1, ArrayPush) \
|
||||
/* Array.prototype.reduce ( callbackfn [ , initialValue ] ) */ \
|
||||
V("reduce", Reduce, 1, INVALID) \
|
||||
V("reduce", Reduce, 1, ArrayReduce) \
|
||||
/* Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) */ \
|
||||
V("reduceRight", ReduceRight, 1, INVALID) \
|
||||
/* Array.prototype.reverse ( ) */ \
|
||||
@ -230,6 +230,9 @@ public:
|
||||
// (4) Array.prototype[@@unscopables]()
|
||||
return GetArrayPrototypeFunctions().Size() + 4;
|
||||
}
|
||||
static JSTaggedValue ReduceUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisHandle,
|
||||
JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, JSMutableHandle<JSTaggedValue> &accumulator,
|
||||
JSHandle<JSTaggedValue> &callbackFnHandle);
|
||||
|
||||
private:
|
||||
#define BUILTIN_ARRAY_FUNCTION_ENTRY(name, method, length, id) \
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "ecmascript/runtime_call_id.h"
|
||||
#include "ecmascript/js_iterator.h"
|
||||
#include "ecmascript/compiler/access_object_stub_builder.h"
|
||||
#include "ecmascript/base/array_helper.h"
|
||||
|
||||
namespace panda::ecmascript::kungfu {
|
||||
void BuiltinsArrayStubBuilder::Concat(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
@ -598,6 +599,262 @@ void BuiltinsArrayStubBuilder::Slice(GateRef glue, GateRef thisValue, GateRef nu
|
||||
}
|
||||
}
|
||||
|
||||
void BuiltinsArrayStubBuilder::Sort(GateRef glue, GateRef thisValue,
|
||||
GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
Label isHeapObject(env);
|
||||
Label isJsArray(env);
|
||||
Label isStability(env);
|
||||
Label defaultConstr(env);
|
||||
Label notCOWArray(env);
|
||||
Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
|
||||
Bind(&isHeapObject);
|
||||
Branch(IsJsArray(thisValue), &isJsArray, slowPath);
|
||||
Bind(&isJsArray);
|
||||
Branch(HasConstructor(thisValue), slowPath, &defaultConstr);
|
||||
Bind(&defaultConstr);
|
||||
Branch(IsStableJSArray(glue, thisValue), &isStability, slowPath);
|
||||
Bind(&isStability);
|
||||
Branch(IsJsCOWArray(thisValue), slowPath, ¬COWArray);
|
||||
Bind(¬COWArray);
|
||||
|
||||
Label argUndefined(env);
|
||||
GateRef callbackFnHandle = GetCallArg0(numArgs);
|
||||
GateRef isUndefined = TaggedIsUndefined(callbackFnHandle);
|
||||
Branch(isUndefined, &argUndefined, slowPath);
|
||||
Bind(&argUndefined);
|
||||
{
|
||||
Label isStableJSArray(env);
|
||||
GateRef stableArray = IsStableJSArray(glue, thisValue);
|
||||
Branch(BoolAnd(stableArray, isUndefined), &isStableJSArray, slowPath);
|
||||
Bind(&isStableJSArray);
|
||||
{
|
||||
GateRef thisEles = GetElementsArray(thisValue);
|
||||
GateRef len = ZExtInt32ToInt64(GetArrayLength(thisValue));
|
||||
DEFVARIABLE(i, VariableType::INT64(), Int64(1));
|
||||
DEFVARIABLE(presentValue, VariableType::JS_ANY(), Undefined());
|
||||
DEFVARIABLE(middleValue, VariableType::JS_ANY(), Undefined());
|
||||
DEFVARIABLE(previousValue, VariableType::JS_ANY(), Undefined());
|
||||
Label loopHead(env);
|
||||
Label loopEnd(env);
|
||||
Label next(env);
|
||||
Label loopExit(env);
|
||||
Jump(&loopHead);
|
||||
LoopBegin(&loopHead);
|
||||
{
|
||||
Branch(Int64LessThan(*i, len), &next, &loopExit);
|
||||
Bind(&next);
|
||||
DEFVARIABLE(beginIndex, VariableType::INT64(), Int64(0));
|
||||
DEFVARIABLE(endIndex, VariableType::INT64(), *i);
|
||||
presentValue = GetValueFromTaggedArray(thisEles, *i);
|
||||
Label loopHead1(env);
|
||||
Label loopEnd1(env);
|
||||
Label next1(env);
|
||||
Label loopExit1(env);
|
||||
Jump(&loopHead1);
|
||||
LoopBegin(&loopHead1);
|
||||
{
|
||||
Branch(Int64LessThan(*beginIndex, *endIndex), &next1, &loopExit1);
|
||||
Bind(&next1);
|
||||
GateRef sum = Int64Add(*beginIndex, *endIndex);
|
||||
GateRef middleIndex = Int64Div(sum, Int64(2)); // 2 : half
|
||||
middleValue = GetValueFromTaggedArray(thisEles, middleIndex);
|
||||
Label isInt(env);
|
||||
Branch(BoolAnd(TaggedIsInt(*middleValue), TaggedIsInt(*presentValue)), &isInt, slowPath);
|
||||
Bind(&isInt);
|
||||
GateRef compareResult =
|
||||
CallNGCRuntime(glue, RTSTUB_ID(FastArraySort), {*middleValue, *presentValue});
|
||||
Label less0(env);
|
||||
Label greater0(env);
|
||||
Branch(Int32LessThanOrEqual(compareResult, Int32(0)), &less0, &greater0);
|
||||
Bind(&greater0);
|
||||
{
|
||||
endIndex = middleIndex;
|
||||
Jump(&loopEnd1);
|
||||
}
|
||||
Bind(&less0);
|
||||
{
|
||||
beginIndex = middleIndex;
|
||||
beginIndex = Int64Add(*beginIndex, Int64(1));
|
||||
Jump(&loopEnd1);
|
||||
}
|
||||
}
|
||||
Bind(&loopEnd1);
|
||||
LoopEnd(&loopHead1);
|
||||
Bind(&loopExit1);
|
||||
|
||||
Label shouldCopy(env);
|
||||
GateRef isGreater0 = Int64GreaterThanOrEqual(*endIndex, Int64(0));
|
||||
GateRef lessI = Int64LessThan(*endIndex, *i);
|
||||
Branch(BoolAnd(isGreater0, lessI), &shouldCopy, &loopEnd);
|
||||
Bind(&shouldCopy);
|
||||
DEFVARIABLE(j, VariableType::INT64(), *i);
|
||||
Label loopHead2(env);
|
||||
Label loopEnd2(env);
|
||||
Label next2(env);
|
||||
Label loopExit2(env);
|
||||
Jump(&loopHead2);
|
||||
LoopBegin(&loopHead2);
|
||||
{
|
||||
Branch(Int64GreaterThan(*j, *endIndex), &next2, &loopExit2);
|
||||
Bind(&next2);
|
||||
previousValue = GetValueFromTaggedArray(thisEles, Int64Sub(*j, Int64(1)));
|
||||
SetValueToTaggedArray(VariableType::JS_ANY(), glue, thisEles, *j, *previousValue);
|
||||
Jump(&loopEnd2);
|
||||
}
|
||||
Bind(&loopEnd2);
|
||||
j = Int64Sub(*j, Int64(1));
|
||||
LoopEnd(&loopHead2);
|
||||
Bind(&loopExit2);
|
||||
SetValueToTaggedArray(VariableType::JS_ANY(), glue, thisEles, *endIndex, *presentValue);
|
||||
Jump(&loopEnd);
|
||||
}
|
||||
Bind(&loopEnd);
|
||||
i = Int64Add(*i, Int64(1));
|
||||
LoopEnd(&loopHead);
|
||||
Bind(&loopExit);
|
||||
result->WriteVariable(thisValue);
|
||||
Jump(exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
|
||||
DEFVARIABLE(thisLen, VariableType::INT32(), Int32(0));
|
||||
|
||||
Label isHeapObject(env);
|
||||
Label isJsArray(env);
|
||||
Label atLeastOneArg(env);
|
||||
Label callbackFnHandleHeapObject(env);
|
||||
Label callbackFnHandleCallable(env);
|
||||
Label noTypeError(env);
|
||||
|
||||
Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
|
||||
Bind(&isHeapObject);
|
||||
Branch(IsJsArray(thisValue), &isJsArray, slowPath);
|
||||
Bind(&isJsArray);
|
||||
thisLen = GetArrayLength(thisValue);
|
||||
Branch(Int64GreaterThanOrEqual(numArgs, IntPtr(1)), &atLeastOneArg, slowPath);
|
||||
Bind(&atLeastOneArg);
|
||||
GateRef callbackFnHandle = GetCallArg0(numArgs);
|
||||
Branch(TaggedIsHeapObject(callbackFnHandle), &callbackFnHandleHeapObject, slowPath);
|
||||
Bind(&callbackFnHandleHeapObject);
|
||||
Branch(IsCallable(callbackFnHandle), &callbackFnHandleCallable, slowPath);
|
||||
Bind(&callbackFnHandleCallable);
|
||||
GateRef thisLenIsZero = Int32Equal(*thisLen, Int32(0));
|
||||
GateRef numArgsLessThanTwo = Int64LessThan(numArgs, IntPtr(2));
|
||||
Branch(BoolAnd(thisLenIsZero, numArgsLessThanTwo), slowPath, &noTypeError);
|
||||
Bind(&noTypeError);
|
||||
{
|
||||
DEFVARIABLE(accumulator, VariableType::JS_ANY(), Undefined());
|
||||
DEFVARIABLE(k, VariableType::INT32(), Int32(0));
|
||||
|
||||
Label updateAccumulator(env);
|
||||
Label checkForStableJSArray(env);
|
||||
|
||||
Branch(Int64Equal(numArgs, IntPtr(2)), &updateAccumulator, slowPath); // 2: provide initialValue param
|
||||
Bind(&updateAccumulator);
|
||||
{
|
||||
accumulator = GetCallArg1(numArgs);
|
||||
Jump(&checkForStableJSArray);
|
||||
}
|
||||
Bind(&checkForStableJSArray);
|
||||
{
|
||||
Label isStableJSArray(env);
|
||||
Label notStableJSArray(env);
|
||||
Branch(IsStableJSArray(glue, thisValue), &isStableJSArray, ¬StableJSArray);
|
||||
Bind(&isStableJSArray);
|
||||
{
|
||||
GateRef argsLength = Int32(4); // 4: «accumulator, kValue, k, thisValue»
|
||||
NewObjectStubBuilder newBuilder(this);
|
||||
GateRef argList = newBuilder.NewTaggedArray(glue, argsLength);
|
||||
Label loopHead(env);
|
||||
Label next(env);
|
||||
Label loopEnd(env);
|
||||
Label loopExit(env);
|
||||
Jump(&loopHead);
|
||||
LoopBegin(&loopHead);
|
||||
{
|
||||
Branch(Int32LessThan(*k, *thisLen), &next, &loopExit);
|
||||
Bind(&next);
|
||||
{
|
||||
Label updateK(env);
|
||||
Label notHole(env);
|
||||
Label changeThisLen(env);
|
||||
Label updateCallResult(env);
|
||||
GateRef elements = GetElementsArray(thisValue);
|
||||
GateRef kValue = GetValueFromTaggedArray(elements, *k);
|
||||
Branch(TaggedIsHole(kValue), &loopEnd, ¬Hole);
|
||||
Bind(¬Hole);
|
||||
{
|
||||
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(0), *accumulator);
|
||||
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(1), kValue);
|
||||
// 2 : parameter location
|
||||
SetValueToTaggedArray(VariableType::INT32(), glue, argList, Int32(2), IntToTaggedInt(*k));
|
||||
// 3 : parameter location
|
||||
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(3), thisValue);
|
||||
GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET));
|
||||
GateRef callResult = JSCallDispatch(glue, callbackFnHandle, argsLength, 0,
|
||||
Circuit::NullGate(), JSCallMode::CALL_THIS_ARGV_WITH_RETURN,
|
||||
{argsLength, argv, Undefined()});
|
||||
GateRef newLen = GetLengthOfTaggedArray(elements);
|
||||
Branch(Int32LessThan(newLen, *thisLen), &changeThisLen, &updateCallResult);
|
||||
Bind(&changeThisLen);
|
||||
{
|
||||
thisLen = newLen;
|
||||
Jump(&updateCallResult);
|
||||
}
|
||||
Bind(&updateCallResult);
|
||||
{
|
||||
accumulator = callResult;
|
||||
Jump(&loopEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Bind(&loopEnd);
|
||||
{
|
||||
k = Int32Add(*k, Int32(1));
|
||||
|
||||
Label isStableJSArray1(env);
|
||||
Label notStableJSArray1(env);
|
||||
Branch(IsStableJSArray(glue, thisValue), &isStableJSArray1, ¬StableJSArray1);
|
||||
Bind(¬StableJSArray1);
|
||||
{
|
||||
Jump(&loopExit);
|
||||
}
|
||||
Bind(&isStableJSArray1);
|
||||
LoopEnd(&loopHead);
|
||||
}
|
||||
Bind(&loopExit);
|
||||
Jump(¬StableJSArray);
|
||||
}
|
||||
Bind(¬StableJSArray);
|
||||
{
|
||||
Label finish(env);
|
||||
Label callRT(env);
|
||||
Branch(Int32LessThan(*k, *thisLen), &callRT, &finish);
|
||||
Bind(&callRT);
|
||||
{
|
||||
accumulator = CallRuntime(glue, RTSTUB_ID(JSArrayReduceUnStable), { thisValue, thisValue,
|
||||
IntToTaggedInt(*k), IntToTaggedInt(*thisLen), *accumulator, callbackFnHandle });
|
||||
Jump(&finish);
|
||||
}
|
||||
Bind(&finish);
|
||||
{
|
||||
result->WriteVariable(*accumulator);
|
||||
Jump(exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: unused arguments are reserved for further development
|
||||
void BuiltinsArrayStubBuilder::Reverse(GateRef glue, GateRef thisValue, [[maybe_unused]] GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath)
|
||||
@ -693,8 +950,11 @@ void BuiltinsArrayStubBuilder::Values(GateRef glue, GateRef thisValue,
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
Label isHeapObject(env);
|
||||
Label isJsArray(env);
|
||||
Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
|
||||
Bind(&isHeapObject);
|
||||
Branch(IsJsArray(thisValue), &isJsArray, slowPath);
|
||||
Bind(&isJsArray);
|
||||
ConstantIndex iterClassIdx = ConstantIndex::JS_ARRAY_ITERATOR_CLASS_INDEX;
|
||||
GateRef iteratorHClass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, iterClassIdx);
|
||||
NewObjectStubBuilder newBuilder(this);
|
||||
@ -775,7 +1035,6 @@ void BuiltinsArrayStubBuilder::Find(GateRef glue, GateRef thisValue, GateRef num
|
||||
Jump(exit);
|
||||
}
|
||||
|
||||
|
||||
void BuiltinsArrayStubBuilder::FindIndex(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath)
|
||||
{
|
||||
|
@ -57,8 +57,14 @@ public:
|
||||
void Slice(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath);
|
||||
|
||||
void Sort(GateRef glue, GateRef thisValue,
|
||||
GateRef numArgs, Variable *result, Label *exit, Label *slowPath);
|
||||
|
||||
void Values(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath);
|
||||
|
||||
void Reduce(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath);
|
||||
|
||||
void Reverse(GateRef glue, GateRef thisValue, GateRef numArgs,
|
||||
Variable *result, Label *exit, Label *slowPath);
|
||||
|
@ -70,6 +70,7 @@ namespace panda::ecmascript::kungfu {
|
||||
V(ArrayPop) \
|
||||
V(ArraySlice) \
|
||||
V(ArrayValues) \
|
||||
V(ArrayReduce) \
|
||||
V(ArrayReverse) \
|
||||
V(ArrayPush) \
|
||||
V(ArrayIncludes) \
|
||||
@ -100,7 +101,8 @@ namespace panda::ecmascript::kungfu {
|
||||
V(ArrayConstructor)
|
||||
|
||||
#define AOT_AND_BUILTINS_STUB_LIST(V) \
|
||||
V(LocaleCompare)
|
||||
V(LocaleCompare) \
|
||||
V(SORT)
|
||||
|
||||
#define AOT_BUILTINS_STUB_LIST(V) \
|
||||
V(SQRT) /* list start and math list start */ \
|
||||
@ -110,7 +112,6 @@ namespace panda::ecmascript::kungfu {
|
||||
V(ATAN) \
|
||||
V(ABS) \
|
||||
V(FLOOR) /* math list end */ \
|
||||
V(SORT) \
|
||||
V(STRINGIFY) \
|
||||
V(MAP_PROTO_ITERATOR) \
|
||||
V(SET_PROTO_ITERATOR) \
|
||||
@ -176,6 +177,7 @@ public:
|
||||
static bool IsTypedBuiltin(ID builtinId)
|
||||
{
|
||||
return (BuiltinsStubCSigns::ID::LocaleCompare == builtinId) ||
|
||||
(BuiltinsStubCSigns::ID::SORT == builtinId) ||
|
||||
((BuiltinsStubCSigns::ID::TYPED_BUILTINS_FIRST <= builtinId) &&
|
||||
(builtinId <= BuiltinsStubCSigns::ID::TYPED_BUILTINS_LAST));
|
||||
}
|
||||
|
@ -290,11 +290,30 @@ DECLARE_BUILTINS(Array##Method)
|
||||
V(LastIndexOf, JS_ANY) \
|
||||
V(Pop, JS_ANY) \
|
||||
V(Slice, JS_POINTER) \
|
||||
V(Reduce, JS_ANY) \
|
||||
V(Reverse, JS_POINTER) \
|
||||
V(Push, JS_ANY) \
|
||||
V(Values, JS_POINTER) \
|
||||
V(Includes, JS_ANY)
|
||||
|
||||
DECLARE_BUILTINS(SORT)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
|
||||
Label exit(env);
|
||||
Label slowPath(env);
|
||||
BuiltinsArrayStubBuilder arrayStubBuilder(this);
|
||||
arrayStubBuilder.Sort(glue, thisValue, numArgs, &res, &exit, &slowPath);
|
||||
Bind(&slowPath);
|
||||
{
|
||||
auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(SORT));
|
||||
res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str());
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(&exit);
|
||||
Return(*res);
|
||||
}
|
||||
|
||||
BUILTINS_WITH_ARRAY_STUB_BUILDER(DECLARE_BUILTINS_WITH_ARRAY_STUB_BUILDER)
|
||||
|
||||
#undef DECLARE_BUILTINS_WITH_ARRAY_STUB_BUILDER
|
||||
|
@ -1223,6 +1223,20 @@ DEF_CALL_SIGNATURE(BigIntEquals)
|
||||
callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC);
|
||||
}
|
||||
|
||||
DEF_CALL_SIGNATURE(FastArraySort)
|
||||
{
|
||||
// 2 : 2 input parameters
|
||||
CallSignature fastArraySort("FastArraySort", 0, 2, ArgumentsOrder::DEFAULT_ORDER, VariableType::INT32());
|
||||
*callSign = fastArraySort;
|
||||
std::array<VariableType, 2> params = { // 2 : 2 input parameters
|
||||
VariableType::JS_ANY(),
|
||||
VariableType::JS_ANY()
|
||||
};
|
||||
callSign->SetParameters(params.data());
|
||||
callSign->SetGCLeafFunction(true);
|
||||
callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC);
|
||||
}
|
||||
|
||||
DEF_CALL_SIGNATURE(LocaleCompareNoGc)
|
||||
{
|
||||
// 4 : 4 input parameters
|
||||
|
@ -474,6 +474,7 @@ private:
|
||||
V(JSHClassFindProtoTransitions) \
|
||||
V(NumberHelperStringToDouble) \
|
||||
V(GetStringToListCacheArray) \
|
||||
V(FastArraySort) \
|
||||
V(LocaleCompareNoGc) \
|
||||
V(StringGetStart) \
|
||||
V(StringGetEnd) \
|
||||
|
@ -249,7 +249,7 @@ void GlobalEnvConstants::InitRootsClass(JSThread *thread, JSHClass *hClass)
|
||||
SetConstant(ConstantIndex::JS_MAP_ITERATOR_CLASS_INDEX,
|
||||
factory->NewEcmaHClass(hClass, JSMapIterator::SIZE, JSType::JS_MAP_ITERATOR, 0)); // 0: no inlined props
|
||||
SetConstant(ConstantIndex::JS_ARRAY_ITERATOR_CLASS_INDEX,
|
||||
factory->NewEcmaHClass(hClass, JSArrayIterator::SIZE, JSType::JS_ARRAY_ITERATOR));
|
||||
factory->NewEcmaHClass(hClass, JSArrayIterator::SIZE, JSType::JS_ARRAY_ITERATOR, 0));
|
||||
SetConstant(
|
||||
ConstantIndex::JS_API_ARRAYLIST_ITERATOR_CLASS_INDEX,
|
||||
factory->NewEcmaHClass(hClass, JSAPIArrayListIterator::SIZE, JSType::JS_API_ARRAYLIST_ITERATOR));
|
||||
|
@ -270,6 +270,31 @@ bool JSTaggedValue::Equal(JSThread *thread, const JSHandle<JSTaggedValue> &x, co
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t CountLeadingZeros(uint32_t value, uint32_t bits)
|
||||
{
|
||||
if (bits == 1) {
|
||||
return value ^ 1;
|
||||
}
|
||||
uint32_t upper_half = value >> (bits / 2); // 2 : half
|
||||
uint32_t nextValue = upper_half != 0 ? upper_half : value;
|
||||
uint32_t add = upper_half != 0 ? 0 : (bits / 2); // 2 : half
|
||||
uint32_t nextBits = bits == 1 ? 1 : bits / 2; // 2 : half
|
||||
return CountLeadingZeros(nextValue, nextBits) + add;
|
||||
}
|
||||
|
||||
const uint32_t kPowersOf10[] = {
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10 * 1000,
|
||||
100 * 1000,
|
||||
1000 * 1000,
|
||||
10 * 1000 * 1000,
|
||||
100 * 1000 * 1000,
|
||||
1000 * 1000 * 1000,
|
||||
};
|
||||
|
||||
int JSTaggedValue::IntLexicographicCompare(JSTaggedValue x, JSTaggedValue y)
|
||||
{
|
||||
ASSERT(x.IsInt() && y.IsInt());
|
||||
@ -294,15 +319,30 @@ int JSTaggedValue::IntLexicographicCompare(JSTaggedValue x, JSTaggedValue y)
|
||||
unsignedX = static_cast<uint32_t>(-xValue);
|
||||
unsignedY = static_cast<uint32_t>(-yValue);
|
||||
}
|
||||
int xDigit = log10(unsignedX);
|
||||
int yDigit = log10(unsignedY);
|
||||
int res;
|
||||
uint32_t bits = sizeof(uint32_t) * 8; // 8 : bits
|
||||
int xLog2 = 31 - CountLeadingZeros(unsignedX, bits); // 31 : Algorithm implementation
|
||||
int xDigit = ((xLog2 + 1) * 1233) >> 12; // 1233 、12 : Algorithm implementation
|
||||
xDigit -= unsignedX < kPowersOf10[xDigit];
|
||||
|
||||
int yLog2 = 31 - CountLeadingZeros(unsignedY, bits); // 31 : Algorithm implementation
|
||||
int yDigit = ((yLog2 + 1) * 1233) >> 12; // 1233 、12 : Algorithm implementation
|
||||
yDigit -= unsignedY < kPowersOf10[yDigit];
|
||||
|
||||
int res = 0;
|
||||
if (xDigit > yDigit) {
|
||||
unsignedY *= pow(10, xDigit - yDigit); // 10: decimal
|
||||
// X has fewer digits. We would like to simply scale up X but that
|
||||
// might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
|
||||
// be scaled up to 9_000_000_000. So we scale up by the next
|
||||
// smallest power and scale down Y to drop one digit. It is OK to
|
||||
// drop one digit from the longer integer since the final digit is
|
||||
// past the length of the shorter integer.
|
||||
unsignedY *= kPowersOf10[xDigit - yDigit - 1];
|
||||
unsignedX /= 10; // 10 : Decimal
|
||||
res = 1;
|
||||
}
|
||||
if (yDigit > xDigit) {
|
||||
unsignedX *= pow(10, yDigit - xDigit); // 10: decimal
|
||||
unsignedX *= kPowersOf10[yDigit - xDigit - 1];
|
||||
unsignedY /= 10; // 10 : Decimal
|
||||
res = -1;
|
||||
}
|
||||
if (unsignedX > unsignedY) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "ecmascript/base/typed_array_helper.h"
|
||||
#include "ecmascript/builtins/builtins_string_iterator.h"
|
||||
#include "ecmascript/compiler/builtins/containers_stub_builder.h"
|
||||
#include "ecmascript/builtins/builtins_array.h"
|
||||
#include "ecmascript/compiler/call_signature.h"
|
||||
#include "ecmascript/compiler/ecma_opcode_des.h"
|
||||
#include "ecmascript/compiler/rt_call_signature.h"
|
||||
@ -52,6 +53,7 @@
|
||||
#include "ecmascript/js_set_iterator.h"
|
||||
#include "ecmascript/js_string_iterator.h"
|
||||
#include "ecmascript/js_thread.h"
|
||||
#include "ecmascript/js_stable_array.h"
|
||||
#include "ecmascript/js_typed_array.h"
|
||||
#include "ecmascript/jspandafile/program_object.h"
|
||||
#include "ecmascript/layout_info.h"
|
||||
@ -67,6 +69,7 @@
|
||||
#include "ecmascript/linked_hash_table.h"
|
||||
#include "ecmascript/builtins/builtins_object.h"
|
||||
#include "libpandafile/bytecode_instruction-inl.h"
|
||||
#include "ecmascript/js_tagged_value.h"
|
||||
#include "macros.h"
|
||||
#ifdef ARK_SUPPORT_INTL
|
||||
#include "ecmascript/js_collator.h"
|
||||
@ -390,6 +393,24 @@ DEF_RUNTIME_STUBS(CheckAndCopyArray)
|
||||
return receiverHandle->GetElements().GetRawData();
|
||||
}
|
||||
|
||||
DEF_RUNTIME_STUBS(JSArrayReduceUnStable)
|
||||
{
|
||||
RUNTIME_STUBS_HEADER(JSArrayReduceUnStable);
|
||||
JSHandle<JSTaggedValue> thisHandle = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the zeroth parameter
|
||||
JSHandle<JSTaggedValue> thisObjVal = GetHArg<JSTaggedValue>(argv, argc, 1); // 1: means the one parameter
|
||||
JSTaggedType taggedValueK = GetTArg(argv, argc, 2); // 2: means the two parameter
|
||||
int64_t k = JSTaggedNumber(JSTaggedValue(taggedValueK)).GetNumber();
|
||||
JSTaggedType taggedValueLen = GetTArg(argv, argc, 3); // 3: means the three parameter
|
||||
int64_t len = JSTaggedNumber(JSTaggedValue(taggedValueLen)).GetNumber();
|
||||
JSMutableHandle<JSTaggedValue> accumulator = JSMutableHandle<JSTaggedValue>(thread,
|
||||
GetHArg<JSTaggedValue>(argv, argc, 4)); // 4: means the four parameter
|
||||
JSHandle<JSTaggedValue> callbackFnHandle = GetHArg<JSTaggedValue>(argv, argc, 5); // 5: means the five parameter
|
||||
|
||||
JSTaggedValue ret = builtins::BuiltinsArray::ReduceUnStableJSArray(thread, thisHandle, thisObjVal, k, len,
|
||||
accumulator, callbackFnHandle);
|
||||
return ret.GetRawData();
|
||||
}
|
||||
|
||||
DEF_RUNTIME_STUBS(JSObjectGrowElementsCapacity)
|
||||
{
|
||||
RUNTIME_STUBS_HEADER(JSObjectGrowElementsCapacity);
|
||||
@ -3004,6 +3025,12 @@ DEF_RUNTIME_STUBS(LocaleCompareWithGc)
|
||||
options, cacheable).GetRawData();
|
||||
}
|
||||
|
||||
int RuntimeStubs::FastArraySort(JSTaggedType x, JSTaggedType y)
|
||||
{
|
||||
DISALLOW_GARBAGE_COLLECTION;
|
||||
return JSTaggedValue::IntLexicographicCompare(JSTaggedValue(x), JSTaggedValue(y));
|
||||
}
|
||||
|
||||
JSTaggedValue RuntimeStubs::LocaleCompareNoGc(uintptr_t argGlue, JSTaggedType locales, EcmaString *thisHandle,
|
||||
EcmaString *thatHandle)
|
||||
{
|
||||
|
@ -133,6 +133,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co
|
||||
V(JSHClassFindProtoTransitions) \
|
||||
V(NumberHelperStringToDouble) \
|
||||
V(GetStringToListCacheArray) \
|
||||
V(FastArraySort) \
|
||||
V(LocaleCompareNoGc) \
|
||||
V(StringGetStart) \
|
||||
V(StringGetEnd) \
|
||||
@ -161,6 +162,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co
|
||||
V(NameDictPutIfAbsent) \
|
||||
V(PropertiesSetValue) \
|
||||
V(TaggedArraySetValue) \
|
||||
V(JSArrayReduceUnStable) \
|
||||
V(CheckAndCopyArray) \
|
||||
V(NewEcmaHClass) \
|
||||
V(UpdateLayOutAndAddTransition) \
|
||||
@ -437,6 +439,7 @@ public:
|
||||
static JSTaggedValue JSHClassFindProtoTransitions(JSHClass *cls, JSTaggedValue key, JSTaggedValue proto);
|
||||
static JSTaggedValue NumberHelperStringToDouble(EcmaString *str);
|
||||
static JSTaggedValue GetStringToListCacheArray(uintptr_t argGlue);
|
||||
static int FastArraySort(JSTaggedType x, JSTaggedType y);
|
||||
static JSTaggedValue LocaleCompareNoGc(uintptr_t argGlue, JSTaggedType locales, EcmaString *thisHandle,
|
||||
EcmaString *thatHandle);
|
||||
static void ArrayTrim(uintptr_t argGlue, TaggedArray *array, int64_t newLength);
|
||||
|
18
test/moduletest/arrayreducecase/BUILD.gn
Normal file
18
test/moduletest/arrayreducecase/BUILD.gn
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import("//arkcompiler/ets_runtime/test/test_helper.gni")
|
||||
|
||||
host_moduletest_action("arrayreducecase") {
|
||||
deps = []
|
||||
}
|
46
test/moduletest/arrayreducecase/arrayreducecase.js
Normal file
46
test/moduletest/arrayreducecase/arrayreducecase.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const array1 = [1, 2, 3, 4];
|
||||
const initialValue = 0;
|
||||
const sumWithInitial = array1.reduce(
|
||||
(accumulator, currentValue) => accumulator + currentValue,
|
||||
initialValue,
|
||||
);
|
||||
print(sumWithInitial);
|
||||
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];
|
||||
const sum = objects.reduce(
|
||||
(accumulator, currentValue) => accumulator + currentValue.x,
|
||||
0,
|
||||
);
|
||||
print(sum); // 6
|
||||
print([1, 2, , 4].reduce((a, b) => a + b)); // 7
|
||||
print([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN
|
||||
const arrayLike = {
|
||||
length: 3,
|
||||
0: 2,
|
||||
1: 3,
|
||||
2: 4,
|
||||
};
|
||||
print(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));
|
||||
const myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
|
||||
const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
|
||||
if (!accumulator.includes(currentValue)) {
|
||||
return [...accumulator, currentValue];
|
||||
}
|
||||
return accumulator;
|
||||
}, []);
|
||||
|
||||
print(myArrayWithNoDuplicates);
|
19
test/moduletest/arrayreducecase/expect_output.txt
Normal file
19
test/moduletest/arrayreducecase/expect_output.txt
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
10
|
||||
6
|
||||
7
|
||||
NaN
|
||||
9
|
||||
a,b,c,e,d
|
18
test/moduletest/arraysortcase/BUILD.gn
Normal file
18
test/moduletest/arraysortcase/BUILD.gn
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import("//arkcompiler/ets_runtime/test/test_helper.gni")
|
||||
|
||||
host_moduletest_action("arraysortcase") {
|
||||
deps = []
|
||||
}
|
56
test/moduletest/arraysortcase/arraysortcase.js
Normal file
56
test/moduletest/arraysortcase/arraysortcase.js
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const months = ['March', 'Jan', 'Feb', 'Dec'];
|
||||
months.sort();
|
||||
print(months);
|
||||
|
||||
const array1 = [1, 30, 4, 21, 100000];
|
||||
array1.sort();
|
||||
print(array1);
|
||||
const numberArray1 = new Array(40, 1, 5, 200);
|
||||
let res1 = numberArray1.sort();
|
||||
print(res1);
|
||||
|
||||
const stringArray = ["Blue", "Humpback", "Beluga"];
|
||||
const numberArray = [40, 1, 5, 200];
|
||||
const numericStringArray = ["80", "9", "700"];
|
||||
const mixedNumericArray = ["80", "9", "700", 40, 1, 5, 200];
|
||||
|
||||
function compareNumbers(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
stringArray.join(); // 'Blue,Humpback,Beluga'
|
||||
stringArray.sort(); // ['Beluga', 'Blue', 'Humpback']
|
||||
print(stringArray)
|
||||
|
||||
numberArray.join(); // '40,1,5,200'
|
||||
numberArray.sort(); // [1, 200, 40, 5]
|
||||
numberArray.sort(compareNumbers); // [1, 5, 40, 200]
|
||||
print(numberArray)
|
||||
|
||||
numericStringArray.join(); // '80,9,700'
|
||||
numericStringArray.sort(); // ['700', '80', '9']
|
||||
numericStringArray.sort(compareNumbers); // ['9', '80', '700']
|
||||
print(numericStringArray)
|
||||
|
||||
mixedNumericArray.join(); // '80,9,700,40,1,5,200'
|
||||
mixedNumericArray.sort(); // [1, 200, 40, 5, '700', '80', '9']
|
||||
mixedNumericArray.sort(compareNumbers); // [1, 5, '9', 40, '80', 200, '700']
|
||||
print(mixedNumericArray)
|
||||
|
||||
print(["a", "c", , "b"].sort()); // ['a', 'b', 'c', empty]
|
||||
print([, undefined, "a", "b"].sort()); // ["a", "b", undefined, empty]
|
22
test/moduletest/arraysortcase/expect_output.txt
Normal file
22
test/moduletest/arraysortcase/expect_output.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
Dec,Feb,Jan,March
|
||||
1,100000,21,30,4
|
||||
1,200,40,5
|
||||
Beluga,Blue,Humpback
|
||||
1,5,40,200
|
||||
9,80,700
|
||||
1,5,9,40,80,200,700
|
||||
a,b,c,
|
||||
a,b,,
|
Loading…
Reference in New Issue
Block a user