Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Array Iterators in javascript #4095

Merged
merged 17 commits into from
Nov 4, 2017

Conversation

meg-gupta
Copy link
Contributor

@meg-gupta meg-gupta commented Oct 30, 2017

This change adds implementation of Javascript's array iterators in Javascript.
This combined with optimization of try finally functions and enabling inlining within functions have try-catch/try-finally improves performance of es6 for-of loops.
For large for-of loops the speedup is more than 2.5x.

@meg-gupta
Copy link
Contributor Author

Commits on top of "Merge conflict errors " are new code. Previous commits have been reviewed before in the builtins branch.

}

let a = o.arrayObj;
var value, done;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var value, done; [](start = 16, length = 16)

initialize this as undefined and true, then you don't need to have it set two places below?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use let instead of var

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akroshg The jit will copy prop the values in either cases. I don't want to add an unconditional write which need not happen.

throw new TypeError("Array Iterator.prototype.next: 'this' is not an Array Iterator object");
}

let a = o.arrayObj;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arrayObj [](start = 26, length = 8)

these are enumerable and visible to the script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to non - enumerable for now.

@obastemur
Copy link
Collaborator

Looks like, there are changes related to OS9049104

}
else {
let index = o.nextIndex;
let len = Array.isArray(a) ? a.length : __chakraLibrary.GetArrayLength(a);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if Array.isArray is overridden?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be bad. Will need an alternative. Thanks.

}

let a = o.arrayObj;
var value, done;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use let instead of var

}

if (!(o instanceof __chakraLibrary.ArrayIterator)) {
throw new TypeError("Array Iterator.prototype.next: 'this' is not an Array Iterator object");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can user override TypeError?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Will need an alternative here. thanks.


o.nextIndex = index + 1;

if (itemKind === 0 /*ArrayIterationKind.Key*/) { // TODO (megupta) : Use clean enums here ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using enums is the approach @dilijev and I have taken on the Intl.js side of things, I think its much cleaner.

var ObjectDefineProperty = function (obj, prop, attributes) {
_objectDefineProperty(obj, prop, setPrototype(attributes, null));
};
var CreateObject = platform.builtInJavascriptObjectCreate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way that we can simplify these aliases for all JsBuiltIns going forward? Like, perhaps we could make the names shorter or have a global standard library written in Javascript that all of these implementation files could use? Something along the lines of https://github.com/Microsoft/ChakraCore/pull/4083/files#diff-fa23cc60499db424efee4a57ac9bafddR105 would be my hope. Having everyone use _.keys or _.defineProperty rather than each files own (hopefully not but possibly) unique alias seems like it would make writing these JsBuiltIns much easier, especially for newer contributors.

#pragma warning(disable:4309) // truncation of constant value
#pragma warning(disable:4838) // conversion from 'int' to 'const char' requires a narrowing conversion

#pragma warning(pop)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional? it looks like the warning setup for including the bytecode, but as far as I know this is a no-op right now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inherited this from the builtins branch and forgot to clean it up! Thanks.

/*
* First parameter is the object onto which prototype should be set; second is the value
*/
Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_SetPrototype(RecyclableObject *function, CallInfo callInfo, ...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way we can share native helpers like this between JsBuiltIns and Intl.js? I know there is a similar function in the Intl extension object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can try moving it to EngineInterfaceObject::InitializeCommonNativeInterfaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to address this as a separate PR. Also - #3962 is incomplete and requires some special handling for EnginerInterfaceObject's deferred type handler.


o.nextIndex = index + 1;

if (itemKind === 0 /*ArrayIterationKind.Key*/) { // TODO (megupta) : Use clean enums here ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

give .Value more priority over .Key

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"scriptType": "eval code",
"lineCount": 1,
"sourceLength": 11
"sourceLength": 11,
"scriptId": 5,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change? Is it due to ChakraLib.js?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to JsBuiltIn.js

@@ -42,6 +42,8 @@ namespace Js
// Need a constructor cache on every function (script and native) to avoid extra checks on the fast path, if the function isn't fixed.
Field(ConstructorCache*) constructorCache;

Field(bool) isJsBuiltInCode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isJsBuiltInCode [](start = 20, length = 15)

I don't see this field is initialized anywhere? did you do that somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was inherited from the builtins branch. The constructor initialization was missing. It is set in the register* functions and used in a few places. Thanks for catching this!


if (callInfo.Count < 2)
{
return scriptContext->GetLibrary()->GetUndefined();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't expect this one right?, the count will always be greater then 2? Should we assert and return 0 instead of undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, converted to assert.


bool JsBuiltInEngineInterfaceExtensionObject::InitializeJsBuiltInNativeInterfaces(DynamicObject * builtInNativeInterfaces, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
{
typeHandler->Convert(builtInNativeInterfaces, mode, 16);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

16 [](start = 60, length = 2)

curious why 16?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its the initSlotCapacity for the SimpleDictionaryTypeHandler. Changed it to 4, I think that suffices.

public:
static NoProfileFunctionInfo JsBuiltIn_RegisterChakraLibraryFunction;
static NoProfileFunctionInfo JsBuiltIn_RegisterFunction;
static NoProfileFunctionInfo JsBuiltIn_Internal_GetArrayLengthFunction;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetLength instead of GetArrayLength (as we are fetching the 'length' property).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@meg-gupta meg-gupta force-pushed the jsarrayitnodelay branch 9 times, most recently from 4772c90 to f971233 Compare November 3, 2017 17:23
@meg-gupta
Copy link
Contributor Author

I have removed the bug fix at #4051 from the PR's commits.

DynamicObject* obj = DynamicObject::FromVar(args.Values[1]);
unsigned propCount = TaggedInt::ToUInt32(args.Values[2]);

Assert(callInfo.Count == 3 + propCount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this, right? this function can be used for other functions in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert will stay the same for all new functions as well. 3 here is this + object on which we want to set property attributes + count of properties

platform.registerChakraLibraryFunction("ArrayIterator", function (arrayObj, iterationKind) {
"use strict";
__chakraLibrary.InitInternalProperties(this, 4, "__$arrayObj$__", "__$nextIndex$__", "__$kind$__", "__$internalDone$__");
this.__$arrayObj$__ = arrayObj;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.$arrayObj$ [](start = 8, length = 19)

make sure you speak with Louis/Ed about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Got approval.

Copy link
Contributor

@akroshg akroshg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Etienne BAUDOUX and others added 13 commits November 3, 2017 14:29
…ebugger trace. Also removed the "arguments" and "caller" property to be spec compliant. I opened an issue about the fact that the built ins methods in C++ are not spec compliant : chakra-core#3043
… the code from the following issue : chakra-core#3140 So BEFORE merging this Pull Request with the Master branch WE MUST fix the issue 3140.
…onfiguration.

- fixed the source context id of the JS built ins.
- Remove IndexOf
- Remove helper C++ functions toLength and toInteger
- Remove IsIn fastpath : found errors in exprgen
@meg-gupta meg-gupta force-pushed the jsarrayitnodelay branch 2 times, most recently from b664d1e to 5b5df92 Compare November 3, 2017 21:42
return __chakraLibrary.CreateArrayIterator(o, 2 /* ArrayIterationKind.KeyAndValue*/);
});

});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

; [](start = 2, length = 1)

nit: newline at end of file and unnecessary blank line above

throw new TypeError("Array.prototype.keys: 'this' is null or undefined");
}
let o = __chakraLibrary.Object(this);
return __chakraLibrary.CreateArrayIterator(o, 0 /* ArrayIterationKind.Key*/);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 /* ArrayIterationKind.Key*/ [](start = 54, length = 29)

Can these magic constants be turned into named constants or and/or members of the Enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot declare these constants within the global function we use to set up the library. It should be stored on the chakraLib object. Its not neat, but I want to avoid storing constants on the chakraLib object.

if (itemKind === 1 /*ArrayIterationKind.Value*/) {
value = a[index];
}
else if (itemKind === 0 /*ArrayIterationKind.Key*/) { // TODO (megupta) : Use clean enums here ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point we probably need to write a JS coding guideline. FWIW the prevailing pattern in library JS is to do } else if (...) { on a single line (no newline between } and else).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I am squashing CR suggestions with my prior commit, please bear with it!

@chakrabot chakrabot merged commit 32196f0 into chakra-core:master Nov 4, 2017
chakrabot pushed a commit that referenced this pull request Nov 4, 2017
Merge pull request #4095 from meg-gupta:jsarrayitnodelay

This change adds implementation of Javascript's array iterators in Javascript.
This combined with optimization of try finally functions and enabling inlining within functions have try-catch/try-finally improves performance of es6 for-of loops.
For large for-of loops the speedup is more than 2.5x.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants