Skip to content

Commit

Permalink
Handle corrupted generator or async generator prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
rhuanjl committed Apr 9, 2021
1 parent a28fef1 commit f69edb2
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 6 deletions.
10 changes: 7 additions & 3 deletions lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,14 @@ Var JavascriptAsyncGeneratorFunction::EntryAsyncGeneratorFunctionImplementation(

auto* asyncGeneratorFn = VarTo<JavascriptAsyncGeneratorFunction>(function);
auto* library = scriptContext->GetLibrary();
auto* prototype = VarTo<DynamicObject>(JavascriptOperators::GetPropertyNoCache(
function, Js::PropertyIds::prototype, scriptContext));
Var prototype = JavascriptOperators::GetPropertyNoCache(function, Js::PropertyIds::prototype, scriptContext);

// fall back to the original prototype if we have an invalid prototype object
DynamicObject* protoObject = VarIs<DynamicObject>(prototype) ?
UnsafeVarTo<DynamicObject>(prototype) : library->GetAsyncGeneratorPrototype();

auto* scriptFn = asyncGeneratorFn->GetGeneratorVirtualScriptFunction();
auto* generator = library->CreateAsyncGenerator(args, scriptFn, prototype);
auto* generator = library->CreateAsyncGenerator(args, scriptFn, protoObject);

// Run the generator to execute until the beginning of the body
if (scriptFn->GetFunctionInfo()->GetGeneratorWithComplexParams())
Expand Down
9 changes: 6 additions & 3 deletions lib/Runtime/Library/JavascriptGeneratorFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,16 @@ using namespace Js;
auto* library = scriptContext->GetLibrary();
auto* generatorFunction = VarTo<JavascriptGeneratorFunction>(function);

DynamicObject* prototype = VarTo<DynamicObject>(JavascriptOperators::GetPropertyNoCache(
function, Js::PropertyIds::prototype, scriptContext));
Var prototype = JavascriptOperators::GetPropertyNoCache(function, Js::PropertyIds::prototype, scriptContext);

// fall back to the original prototype if we have an invalid prototype object
DynamicObject* protoObject = VarIs<DynamicObject>(prototype) ?
UnsafeVarTo<DynamicObject>(prototype) : library->GetGeneratorPrototype();

JavascriptGenerator* generator = library->CreateGenerator(
args,
generatorFunction->scriptFunction,
prototype);
protoObject);

// Call a next on the generator to execute till the beginning of the body
FunctionInfo* funcInfo = generatorFunction->scriptFunction->GetFunctionInfo();
Expand Down
3 changes: 3 additions & 0 deletions lib/Runtime/Library/JavascriptLibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,9 @@ namespace Js
DynamicObject* GetWebAssemblyLinkErrorPrototype() const { return webAssemblyLinkErrorPrototype; }
DynamicObject* GetWebAssemblyLinkErrorConstructor() const { return webAssemblyLinkErrorConstructor; }

DynamicObject* GetAsyncGeneratorPrototype() const { return asyncGeneratorPrototype; }
DynamicObject* GetGeneratorPrototype() const { return generatorPrototype; }

DynamicObject* GetChakraLib() const { return chakraLibraryObject; }

#if ENABLE_TTD
Expand Down
23 changes: 23 additions & 0 deletions test/es6/generators-functionality.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -56,6 +57,28 @@ function simpleThrowFunc() {
var global = (function() { return this; }());

var tests = [
{
name: "Generator function with overwritten prototype",
body: function () {
function* gf() {};
var gfp = gf.prototype;
assert.strictEqual(gf().__proto__, gfp, "Generator function uses prototype.");
var obj = {};
gf.prototype = obj;
assert.strictEqual(gf().__proto__, obj, "Generator function uses overwritten prototype.");
gf.prototype = 1;
assert.areEqual(gf().__proto__.toString(), gfp.toString(), "Generator function falls back to original prototype.");
if (gf().__proto__ === gfp) { assert.error("Original prototype should not be same object as gfp")}
var originalGfp = gf().__proto__;
assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype.");
gf.prototype = 0;
assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype.");
gf.prototype = "hi";
assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype.");
delete gfp.prototype;
assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype.");
}
},
{
name: "Simple generator functions with no parameters or locals or captures",
body: function () {
Expand Down
23 changes: 23 additions & 0 deletions test/es7/async-generator-apis.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -30,6 +31,28 @@ function testMethod(object, name, objectName, methodName = name) {
}

const tests = [
{
name: "Async Generator function with overwritten prototype",
body: function () {
async function* agf() {};
var gfp = agf.prototype;
assert.strictEqual(agf().__proto__, gfp, "Async Generator function uses prototype.");
var obj = {};
agf.prototype = obj;
assert.strictEqual(agf().__proto__, obj, "Async Generator function uses overwritten prototype.");
agf.prototype = 1;
assert.areEqual(agf().__proto__.toString(), gfp.toString(), "Generator function falls back to original prototype.");
if (agf().__proto__ === gfp) { assert.error("Original prototype should not be same object as gfp")}
var originalGfp = agf().__proto__;
assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
agf.prototype = 0;
assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
agf.prototype = "hi";
assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
delete gfp.prototype;
assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
}
},
{
name: "AsyncGeneratorFunction is not exposed on the global object",
body: function () {
Expand Down

0 comments on commit f69edb2

Please sign in to comment.