Skip to content

Commit

Permalink
[MERGE #4267 @obastemur] jsrt: JsCreateExternalObjectWithPrototype
Browse files Browse the repository at this point in the history
Merge pull request #4267 from obastemur:Jsrt_external_with_proto

node-chakracore, each `ObjectTemplate` creation first creates an external object and sets prototype separately. `JsSetPrototype` is decently expensive to lazy call.

This new `JsCreateExternalObjectWithPrototype` should supersede `JsCreateExternalObject` since prototype can be provided as `nullptr` if there is none.

Also VS export Jsrt***ObjectProperty
  • Loading branch information
obastemur committed Nov 20, 2017
2 parents 2cc975f + 7bf59d1 commit ebd118a
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 5 deletions.
22 changes: 22 additions & 0 deletions lib/Jsrt/ChakraCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,28 @@ CHAKRA_API
_In_ JsValueRef object2,
_Out_ bool *result);

/// <summary>
/// Creates a new object (with prototype) that stores some external data.
/// </summary>
/// <remarks>
/// Requires an active script context.
/// </remarks>
/// <param name="data">External data that the object will represent. May be null.</param>
/// <param name="finalizeCallback">
/// A callback for when the object is finalized. May be null.
/// </param>
/// <param name="prototype">Prototype object or nullptr.</param>
/// <param name="object">The new object.</param>
/// <returns>
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
/// </returns>
CHAKRA_API
JsCreateExternalObjectWithPrototype(
_In_opt_ void *data,
_In_opt_ JsFinalizeCallback finalizeCallback,
_In_ JsValueRef prototype,
_Out_ JsValueRef *object);

/// <summary>
/// Gets an object's property.
/// </summary>
Expand Down
36 changes: 34 additions & 2 deletions lib/Jsrt/Jsrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ void CALLBACK CreateExternalObject_TTDCallback(Js::ScriptContext* ctx, Js::Var*
{
TTDAssert(object != nullptr, "This should always be a valid location");

*object = JsrtExternalObject::Create(nullptr, nullptr, ctx);
*object = JsrtExternalObject::Create(nullptr, nullptr, nullptr, ctx);
}

void CALLBACK TTDDummyPromiseContinuationCallback(JsValueRef task, void *callbackState)
Expand Down Expand Up @@ -1285,14 +1285,46 @@ CHAKRA_API JsCreateExternalObject(_In_opt_ void *data, _In_opt_ JsFinalizeCallba

PARAM_NOT_NULL(object);

*object = JsrtExternalObject::Create(data, finalizeCallback, scriptContext);
*object = JsrtExternalObject::Create(data, finalizeCallback, nullptr, scriptContext);

PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, object);

return JsNoError;
});
}

#ifndef NTBUILD
CHAKRA_API JsCreateExternalObjectWithPrototype(_In_opt_ void *data,
_In_opt_ JsFinalizeCallback finalizeCallback,
_In_ JsValueRef prototype,
_Out_ JsValueRef *object)
{
return ContextAPINoScriptWrapper([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTAllocateExternalObject);

PARAM_NOT_NULL(object);

Js::RecyclableObject * prototypeObject = nullptr;
if (prototype != nullptr)
{
VALIDATE_INCOMING_OBJECT(prototype, scriptContext);
prototypeObject = Js::RecyclableObject::FromVar(prototype);
}

*object = JsrtExternalObject::Create(data, finalizeCallback, prototypeObject, scriptContext);

PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, object);

if (prototypeObject != nullptr)
{
PERFORM_JSRT_TTD_RECORD_ACTION(scriptContext, RecordJsRTSetPrototype, *object, prototypeObject);
}

return JsNoError;
});
}
#endif

CHAKRA_API JsConvertValueToObject(_In_ JsValueRef value, _Out_ JsValueRef *result)
{
return ContextAPIWrapper<JSRT_MAYBE_TRUE>([&] (Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
Expand Down
8 changes: 8 additions & 0 deletions lib/Jsrt/JsrtCommonExports.inc
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,12 @@
JsHasOwnProperty
JsCopyStringOneByte
JsGetDataViewInfo
JsCreateExternalObjectWithPrototype
JsObjectGetProperty
JsObjectHasProperty
JsObjectSetProperty
JsObjectDeleteProperty
JsObjectHasOwnProperty
JsObjectGetOwnPropertyDescriptor
JsObjectDefineProperty
#endif
11 changes: 9 additions & 2 deletions lib/Jsrt/JsrtExternalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ JsrtExternalObject::JsrtExternalObject(JsrtExternalType * type, void *data) :
}

/* static */
JsrtExternalObject* JsrtExternalObject::Create(void *data, JsFinalizeCallback finalizeCallback, Js::ScriptContext *scriptContext)
JsrtExternalObject* JsrtExternalObject::Create(void *data, JsFinalizeCallback finalizeCallback, Js::RecyclableObject * prototype, Js::ScriptContext *scriptContext)
{
Js::DynamicType * dynamicType = scriptContext->GetLibrary()->GetCachedJsrtExternalType(reinterpret_cast<uintptr_t>(finalizeCallback));

Expand All @@ -41,7 +41,14 @@ JsrtExternalObject* JsrtExternalObject::Create(void *data, JsFinalizeCallback fi
Assert(dynamicType->IsJsrtExternal());
Assert(dynamicType->GetIsShared());

return RecyclerNewFinalized(scriptContext->GetRecycler(), JsrtExternalObject, static_cast<JsrtExternalType*>(dynamicType), data);
JsrtExternalObject * externalObject = RecyclerNewFinalized(scriptContext->GetRecycler(), JsrtExternalObject, static_cast<JsrtExternalType*>(dynamicType), data);

if (prototype != nullptr)
{
externalObject->SetPrototype(prototype);
}

return externalObject;
}

bool JsrtExternalObject::Is(Js::Var value)
Expand Down
2 changes: 1 addition & 1 deletion lib/Jsrt/JsrtExternalObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class JsrtExternalObject : public Js::DynamicObject
static bool Is(Js::Var value);
static JsrtExternalObject * FromVar(Js::Var value);
static JsrtExternalObject * UnsafeFromVar(Js::Var value);
static JsrtExternalObject * Create(void *data, JsFinalizeCallback finalizeCallback, Js::ScriptContext *scriptContext);
static JsrtExternalObject * Create(void *data, JsFinalizeCallback finalizeCallback, Js::RecyclableObject * prototype, Js::ScriptContext *scriptContext);

JsrtExternalType * GetExternalType() const { return (JsrtExternalType *)this->GetType(); }

Expand Down
52 changes: 52 additions & 0 deletions test/native-tests/test-static-external/Platform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

var isWindows = !WScript.Platform || WScript.Platform.OS == 'win32';
var path_sep = isWindows ? '\\' : '/';
var isStaticBuild = WScript.Platform && WScript.Platform.LINK_TYPE == 'static';

if (!isStaticBuild) {
// test will be ignored
print("# IGNORE_THIS_TEST");
} else {
var platform = WScript.Platform.OS;
var binaryPath = WScript.Platform.BINARY_PATH;
// discard `ch` from path
binaryPath = binaryPath.substr(0, binaryPath.lastIndexOf(path_sep));
var makefile =
"IDIR=" + binaryPath + "/../../lib/Jsrt \n\
\n\
LIBRARY_PATH=" + binaryPath + "/lib\n\
PLATFORM=" + platform + "\n\
LDIR=$(LIBRARY_PATH)/libChakraCoreStatic.a \n\
\n\
ifeq (darwin, ${PLATFORM})\n\
\tICU4C_LIBRARY_PATH ?= /usr/local/opt/icu4c\n\
\tCFLAGS=-lstdc++ -std=c++11 -I$(IDIR)\n\
\tFORCE_STARTS=-Wl,-force_load,\n\
\tFORCE_ENDS=\n\
\tLIBS=-framework CoreFoundation -framework Security -lm -ldl -Wno-c++11-compat-deprecated-writable-strings \
-Wno-deprecated-declarations -Wno-unknown-warning-option -o sample.o\n\
\tLDIR+=$(ICU4C_LIBRARY_PATH)/lib/libicudata.a \
$(ICU4C_LIBRARY_PATH)/lib/libicuuc.a \
$(ICU4C_LIBRARY_PATH)/lib/libicui18n.a\n\
else\n\
\tCFLAGS=-lstdc++ -std=c++0x -I$(IDIR)\n\
\tFORCE_STARTS=-Wl,--whole-archive\n\
\tFORCE_ENDS=-Wl,--no-whole-archive\n\
\tLIBS=-pthread -lm -ldl -licuuc -Wno-c++11-compat-deprecated-writable-strings \
-Wno-deprecated-declarations -Wno-unknown-warning-option -o sample.o\n\
endif\n\
\n\
testmake:\n\
\t$(CC) sample.cpp $(CFLAGS) $(FORCE_STARTS) $(LDIR) $(FORCE_ENDS) $(LIBS)\n\
\n\
.PHONY: clean\n\
\n\
clean:\n\
\trm sample.o\n";

print(makefile)
}
153 changes: 153 additions & 0 deletions test/native-tests/test-static-external/sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

#include "ChakraCore.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <cstring>

#define FAIL_CHECK(cmd) \
do \
{ \
JsErrorCode errCode = (JsErrorCode) cmd; \
if (errCode != JsNoError) \
{ \
printf("Error %d at '%s'\n", \
errCode, #cmd); \
return 1; \
} \
} while(0)

using namespace std;

unsigned int AddMethodProperty(JsValueRef target, const char* str,
JsNativeFunction func)
{
JsPropertyIdRef Id;
FAIL_CHECK(JsCreatePropertyId(str, strlen(str), &Id));

JsValueRef nameVar;
FAIL_CHECK(JsCreateString(str, strlen(str), &nameVar));

JsValueRef functionVar;
FAIL_CHECK(JsCreateNamedFunction(nameVar, func, nullptr, &functionVar));

FAIL_CHECK(JsSetProperty(target, Id, functionVar, true));

return JsNoError;
}

JsValueRef CHECK(JsValueRef callee, bool isConstructCall,
JsValueRef * arguments, unsigned short argumentCount, void * callbackState)
{
void * data;
if (JsGetExternalData(arguments[1], &data) == JsNoError &&
((size_t*)data)[122] - ((size_t*)data)[0] == 41976)
{
fprintf(stdout, "SU"); // CCESS will be printed during finalizer call
}
else
{
fprintf(stderr, "External data corrupt?");
}

return nullptr;
}

JsValueRef createWithProto(JsValueRef callee, bool isConstructCall,
JsValueRef * arguments, unsigned short argumentCount, void * callbackState)
{
size_t *data = new size_t[123];
data[0] = 12345;
data[122] = 54321;

JsValueRef obj;

JsErrorCode code = JsCreateExternalObjectWithPrototype(data, [](void* buffer)
{
size_t * mem = (size_t*)buffer;
if(!(mem[122] - mem[0] == 41976)) abort();
delete mem;

// test tool needs to see `SUCCESS` to determine if script was
// successful. So far the test printed `SU`,
// this should print the last part `CCESS`
fprintf(stdout, "CCESS\n");
}, arguments[1], &obj);

if (code != JsNoError)
{
printf("FAILED to create external object %d \n", code);
}

return obj;
}

int main()
{
JsRuntimeHandle runtime;
JsContextRef context;
JsValueRef result;
unsigned currentSourceContext = 0;

const char* script = "\
(function(){\
var proto = { sub: 3 }; \
var ext = CreateWithProto(proto); \
if (ext.sub === 3) CHECK(ext); \
return \'\'; \
})();\
";

// Create a runtime.
JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime);

// Create an execution context.
JsCreateContext(runtime, &context);

// Now set the current execution context.
JsSetCurrentContext(context);

JsValueRef fname;
FAIL_CHECK(JsCreateString("sample", strlen("sample"), &fname));

JsValueRef scriptSource;
FAIL_CHECK(JsCreateExternalArrayBuffer((void*)script,
(unsigned)strlen(script),
nullptr, nullptr, &scriptSource));

JsValueRef global;
FAIL_CHECK(JsGetGlobalObject(&global));

FAIL_CHECK(AddMethodProperty(global, "CreateWithProto", &createWithProto));
FAIL_CHECK(AddMethodProperty(global, "CHECK", &CHECK));

// Run the script.
FAIL_CHECK(JsRun(scriptSource, currentSourceContext++, fname,
JsParseScriptAttributeNone, &result));

// Convert your script result to String in JavaScript;
// redundant if your script returns a String
JsValueRef resultJSString;
FAIL_CHECK(JsConvertValueToString(result, &resultJSString));

// Project script result back to C++.
char *resultSTR = nullptr;
size_t stringLength;
FAIL_CHECK(JsCopyString(resultJSString, nullptr, 0, &stringLength));
resultSTR = (char*)malloc(stringLength + 1);
FAIL_CHECK(JsCopyString(resultJSString, resultSTR, stringLength, nullptr));
resultSTR[stringLength] = 0;

fprintf(stdout, "%s", resultSTR); // prints an empty string ``
free(resultSTR);

// Dispose runtime
JsSetCurrentContext(JS_INVALID_REFERENCE);
JsDisposeRuntime(runtime);

return 0;
}

0 comments on commit ebd118a

Please sign in to comment.