-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
jsrt: JsObject Delete/Get/Has/OwnP../Set Property #3875
Conversation
99be27c
to
e39729a
Compare
lib/Common/Codex/Utf8Helper.h
Outdated
/// | ||
template <typename AllocatorFunction> | ||
HRESULT NarrowStringToWide(_In_ AllocatorFunction allocator,_In_ LPCSTR sourceString, size_t sourceCount, _Out_ LPWSTR* destStringPtr, _Out_ size_t* destCount, size_t* allocateCount = nullptr) | ||
inline HRESULT NarrowStringToWideNoAlloc(_In_ LPCSTR sourceString, size_t sourceCount, _Out_ LPWSTR destString, _Out_ size_t* destCount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the no-alloc case, you should pass in the size of destString
so we can check for and avoid overflows.
lib/Jsrt/Jsrt.cpp
Outdated
AssertMsg(scriptContext->GetThreadContext()->IsScriptActive(), "Caller is expected to be under ContextAPIWrapper!"); | ||
PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTGetProperty, propertyRecord, object); | ||
|
||
*value = Js::JavascriptOperators::GetPropertyNoCache(object, propertyRecord->GetPropertyId(), scriptContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why GetPropertyNoCache
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We know the type and we don't have cache info ptr. So, don't waste time checking the type again and doing cache related stuff.
lib/Jsrt/Jsrt.cpp
Outdated
@@ -1367,102 +1367,199 @@ CHAKRA_API JsPreventExtension(_In_ JsValueRef object) | |||
}); | |||
} | |||
|
|||
static JsErrorCode InternalGetPropertyRecord(Js::ScriptContext * scriptContext, JsValueRef key, _Out_ const Js::PropertyRecord ** propertyRecord) | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this deliberately weaker than being able to compute x[y]
, e.g. var y = 2, var x = [1,2,3]; x[y]
? That is we deliberately do not support getting property records for numbers, null, true, false, undefined, or arbitrary objects which get converted to a string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an interface for number JsGetIndexed...
(also works similarly) for the time being.. Once / if we ever drop that interface, we may combine them here. I was designing in a way that they are handled in the same place. Then I realized, consumer may have a better chance to know property type hence no point wasting it here.
c8c22f6
to
3a9a41d
Compare
@dotnet-bot test Ubuntu shared_ubuntu_linux_debug please |
3a9a41d
to
72872dc
Compare
lib/Runtime/Library/ConcatString.h
Outdated
@@ -9,18 +9,30 @@ namespace Js | |||
class LiteralStringWithPropertyStringPtr : public LiteralString | |||
{ | |||
private: | |||
// TODO: Can we make PropertyString && LiteralStringWithPropertyStringPtr share the string buffer? | |||
PropertyString * propertyString; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MikeHolman do you know why this isn't a Field
? The CI seems to be complaining about it now, and I'm inclined to agree with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it should be one.
propertyRecord = propertyString->GetPropertyRecord(); | ||
} | ||
|
||
propertyString = str->GetPropertyStringLazy(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not also use GetPropertyStringLazy
in OP_GetElementI
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? sorry not sure I understood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also looks like JavascriptConversion::ToPropertyKey
could have some of it's code dealing with LiteralStringWithPropertyStringPtr
replaced with a call to GetPropertyStringLazy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JavascriptConversion::ToPropertyKey
and similar places good idea. Could you please clarify -> Why not also use GetPropertyStringLazy in OP_GetElementI
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was looking for where you had used GetPropertyStringLazy
, and I saw some other places that GetPropertyString
was used but you had not changed it to use GetPropertyStringLazy
; is there a reason for that, or should we also update the other locations? Specifically for OP_GetElementI
I was referring to https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Language/JavascriptOperators.cpp#L3697
lib/Common/Codex/Utf8Helper.h
Outdated
if (allocateCount != nullptr) *allocateCount = cbDestString; | ||
|
||
*destStringPtr = destString; | ||
HRESULT result = NarrowStringToWideNoAlloc(sourceString, sourceCount, destString, sourceCount, destCount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that the destBufferCount
parameter here should be sourceCount+1
to allow for the null
72872dc
to
547fd6f
Compare
/cc @mrkmarron are you happy with the TTD changes? /cc @liminzhu comments on new JSRT? |
lib/Runtime/Library/ConcatString.h
Outdated
|
||
public: | ||
PropertyString * GetPropertyString() const; | ||
PropertyString * GetPropertyStringLazy(); // Get if it's there, otherwise bring it in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this name is confusing to me. i think we could come up with something better, maybe GetOrAddPropertyString
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not strong on naming and what you suggest sounds good.
lib/Runtime/Library/ConcatString.h
Outdated
@@ -9,18 +9,30 @@ namespace Js | |||
class LiteralStringWithPropertyStringPtr : public LiteralString | |||
{ | |||
private: | |||
PropertyString * propertyString; | |||
// TODO: Can we make PropertyString && LiteralStringWithPropertyStringPtr share the string buffer? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just overwrite the buffer on this once we populate the PropertyString field (and then PropertyString, LiteralStringWithPropertyStringPtr, and PropertyRecord will all be sharing same buffer)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, this is a trivial TODO. However, I would like to keep it separate. (That's why TODO) BTW; the issue is still open and it also mentions this improvement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
lib/Runtime/Library/ConcatString.cpp
Outdated
switch (charCount) | ||
{ | ||
case 0: | ||
return (LiteralStringWithPropertyStringPtr*) library->GetEmptyString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you have GetEmptyString return LiteralStringWithPropertyStringPtr* so we can avoid this cast? otherwise someone could change the emptyString to some other type of string, and compiler won't complain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could put an assert on the returning type (VT check)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure that would work, but I think it is strictly better to have more type information represented at compile time rather than relying on run time asserts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't we going to cast this type eventually? (library-GetEmptyString)
Just FYI I have some changes in master that this will conflict with (refactoring, nothing major). Also, any reason this is going in 1.7? Seems odd to be doing perf work in a release branch. |
We should have an equivalent for JsHasOwnProperty for completeness. Also, do those APIs work with symbols? |
and stressful.. |
547fd6f
to
c803ffc
Compare
TTD parts look good to me. I am not sure if they have been run through the TTD top NPM tests yet which will test this API more thoroughly. |
self note for the PR;
|
e651dc7
to
fea3125
Compare
Made some additional improvements. Will measure the perf separately but need review on this. |
fea3125
to
518c4f9
Compare
lib/Common/Codex/Utf8Helper.h
Outdated
return E_OUTOFMEMORY; | ||
} | ||
|
||
WCHAR* destString = (WCHAR*)allocator(cbDestString); | ||
if (destString == nullptr) | ||
if (destString == nullptr || sourceCount >= destBufferCount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If destString == nullptr
then we shouldn't be writing to it at all; perhaps put that check first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also isn't sourceCount >= destBufferCount
legal? E.g. if the input narrow string has more 3-byte utf8 codepoints than 1-bye utf8 codeopints, the utf16 representation can be shorter than the input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand your first comment.
Second comment; -> sourceCount represents the length of char16
buffer. Allocator is supposed to allocate equal or more (prior to calculating exact correspondence). So we can convert using the fast path below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is saying if (destString == nullptr || ...) { destString[0] = 0}
which will (try to) write to the null pointer. Also the previous condition will write to destString
without checking whether it is null. We should either be checking if destString == nullptr
up front and exiting with an error without writing to it, or we should be relying on SAL to make sure that we never pass in a null pointer I think.
Regarding the second point; Requiring that sourceCount >= destBufferCount
is safe, but it may lead to excess memory usage. Prior to this PR the over-estimating allocations are (I believe) always temporary, and the string is copied to a more accurately sized buffer for long-term storage, or freed once the variable goes out of scope. The new usage that you're adding in recycler-allocates a (potentially) over-estimated amount and hands that off to to be stored in a LiteralString
.
Perhaps it is worth being clear about what the contract of this function is? Are you supposed to supply a buffer with destBufferCount == sourceCount
, and then the caller is responsible for adjusting to destCount
when the call returns? Is the caller supposed to supply a buffer with size at least destCount
which is initially unknown, and it is an error if destBufferCount < destCount
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second part; Yes, the idea is/was, instead of double pass, allocate ASCII amount of memory. OR we will endup having double pass for all the strings.
@@ -142,14 +134,44 @@ namespace utf8 | |||
// instead of replacing them with the "replacement" chracter. Pass a flag to our | |||
// decoder to require such behavior | |||
utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(remDestString, remSourceString, (LPCUTF8) sourceString + cbSourceString, DecodeOptions::doAllowInvalidWCHARs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assert just before here that cchDestString <= sourceCount
should be cchDestString <= destBufferCount
I believe (maybe strictly less to allow for the null terminator), and if this function is exposed outside of our code then I think it should be an if(...) { return E_OUTOFMEMORY;}
and not an assert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure.. This PR didn't make any logical changes within the Utf8Helper stuff. Just split an existing interface for reuse purposes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added if (cchDestString > sourceCount)
79487c9
to
cfaa57b
Compare
{ | ||
return CheckPrototypesForAccessorOrNonWritablePropertyCore<JavascriptString*, true, false>(instance, propertyNameString, setterValue, flags, info, scriptContext); | ||
} | ||
PropertyString * propertyString = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable isn't used and that's causing some compile errors on windows.
cfaa57b
to
48dfd00
Compare
Made some additional changes, looks like |
48dfd00
to
7caf634
Compare
lib/Runtime/Library/JSON.cpp
Outdated
@@ -431,10 +431,16 @@ namespace JSON | |||
|
|||
if (value == nullptr) | |||
{ | |||
Js::PropertyString* propertyString = Js::PropertyString::TryFromVar(key); | |||
if (propertyString) | |||
Js::JavascriptString * propertyKey = key; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to conflict with another JSON change that went in earlier today, might want to rebase your change and reapply if necessary
AcmeAIR LTO gain (3%) New JSRT property interface that API user can use without going through convert-to-property-id process. See # 3790 for details. Also added a TODO note; ``` TODO: Can we make PropertyString && LiteralStringWithPropertyStringPtr share the string buffer? ``` Once this PR is merged, there will be an additional PR on node-chakracore end to benefit this new interface.
7caf634
to
fc6240e
Compare
…perty Merge pull request #3875 from obastemur:desert_cerodon AcmeAIR LTO gain (3%) New JSRT property interface that API user can use without going through convert-to-property-id process. See #3790 for details. Also added a note; ``` TODO: Can we make PropertyString && LiteralStringWithPropertyStringPtr share the string buffer? ``` Once this PR is merged, there will be an additional PR on node-chakracore end to benefit this new interface.
Merge pull request #4123 from obastemur:lesscmp See issue details #4110 - Fix unnecessary string comparison with PropertyRecord/Equal (acme-air hits often) - Improve BuiltInPropertyRecord string comparison (acme-air hits 3 times per request) This PR needs #3875 to be merged first. Results from micro-`test/benchmark` are flat.
AcmeAIR LTO gain (3%)
New JSRT property interface that API user can use without going through convert-to-property-id process.
See #3790 for details.
Also added a note;
Once this PR is merged, there will be an additional PR on node-chakracore end to benefit this new interface.