Skip to content
Permalink
Browse files
[WebCore][JSC] Use new @throwTypeError and @throwRangeError intrinsics
https://bugs.webkit.org/show_bug.cgi?id=163001

Reviewed by Keith Miller.

Source/JavaScriptCore:

Previously, the argument of @throwXXXError intrinsics must be string literal.
But it is error-prone restriction. This patch relaxes the restriction to accept
arbitrary values. To keep emitted bytecode small, if the argument is string literal,
we generate the same bytecode as before. If the argument is not string literal,
we evaluate it and perform to_string before passing to throw_static_error.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitThrowStaticError):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_throwTypeError):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_throwRangeError):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):

Source/WebCore:

Replace `throw new @xxxerror(...)` to @throwXXXError intrinsic.
It reduces the size of bytecode sequence and facilitate inlining.

No behavior change.

* Modules/fetch/FetchHeaders.js:
(initializeFetchHeaders):
* Modules/fetch/FetchInternals.js:
(fillFetchHeaders):
* Modules/fetch/FetchRequest.js:
(initializeFetchRequest):
* Modules/fetch/FetchResponse.js:
(initializeFetchResponse):
(clone):
* Modules/mediastream/NavigatorUserMedia.js:
(webkitGetUserMedia):
* Modules/mediastream/RTCPeerConnection.js:
(initializeRTCPeerConnection):
(getLocalStreams):
(getStreamById):
(addStream):
* Modules/streams/ReadableStream.js:
(initializeReadableStream):
(getReader):
* Modules/streams/ReadableStreamDefaultController.js:
(enqueue):
(error):
(close):
* Modules/streams/ReadableStreamDefaultReader.js:
(releaseLock):
* Modules/streams/ReadableStreamInternals.js:
(privateInitializeReadableStreamDefaultReader):
(privateInitializeReadableStreamDefaultController):
(doStructuredClone):
(readableStreamError):
* Modules/streams/StreamInternals.js:
(validateAndNormalizeQueuingStrategy):
(enqueueValueWithSize):
* Modules/streams/WritableStream.js:
(initializeWritableStream):
(state):
* xml/XMLHttpRequest.js:
(response):


Canonical link: https://commits.webkit.org/180923@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@206870 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Oct 6, 2016
1 parent 3244e06 commit 30070a41202a33dd18e6852a8e776c5ed8a82549
@@ -1,3 +1,25 @@
2016-10-06 Yusuke Suzuki <utatane.tea@gmail.com>

[WebCore][JSC] Use new @throwTypeError and @throwRangeError intrinsics
https://bugs.webkit.org/show_bug.cgi?id=163001

Reviewed by Keith Miller.

Previously, the argument of @throwXXXError intrinsics must be string literal.
But it is error-prone restriction. This patch relaxes the restriction to accept
arbitrary values. To keep emitted bytecode small, if the argument is string literal,
we generate the same bytecode as before. If the argument is not string literal,
we evaluate it and perform to_string before passing to throw_static_error.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitThrowStaticError):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_throwTypeError):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_throwRangeError):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):

2016-10-05 Yusuke Suzuki <utatane.tea@gmail.com>

[JSC] Add @throwXXXError bytecode intrinsic
@@ -3905,6 +3905,15 @@ int BytecodeGenerator::labelScopeDepth() const
return localScopeDepth() + m_finallyDepth;
}

void BytecodeGenerator::emitThrowStaticError(ErrorType errorType, RegisterID* raw)
{
RefPtr<RegisterID> message = newTemporary();
emitToString(message.get(), raw);
emitOpcode(op_throw_static_error);
instructions().append(message->index());
instructions().append(static_cast<unsigned>(errorType));
}

void BytecodeGenerator::emitThrowStaticError(ErrorType errorType, const Identifier& message)
{
emitOpcode(op_throw_static_error);
@@ -662,6 +662,7 @@ namespace JSC {
emitUnaryNoDstOp(op_throw, exc);
}

void emitThrowStaticError(ErrorType, RegisterID*);
void emitThrowStaticError(ErrorType, const Identifier& message);
void emitThrowReferenceError(const String& message);
void emitThrowTypeError(const String& message);
@@ -901,22 +901,29 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_tailCallForwardArguments(Bytec
RegisterID* BytecodeIntrinsicNode::emit_intrinsic_throwTypeError(BytecodeGenerator& generator, RegisterID* dst)
{
ArgumentListNode* node = m_args->m_listNode;
ASSERT(node->m_expr->isString());
const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
ASSERT(!node->m_next);

generator.emitThrowTypeError(ident);
if (node->m_expr->isString()) {
const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
generator.emitThrowTypeError(ident);
} else {
RefPtr<RegisterID> message = generator.emitNode(node);
generator.emitThrowStaticError(ErrorType::TypeError, message.get());
}
return dst;
}

RegisterID* BytecodeIntrinsicNode::emit_intrinsic_throwRangeError(BytecodeGenerator& generator, RegisterID* dst)
{
ArgumentListNode* node = m_args->m_listNode;
ASSERT(node->m_expr->isString());
const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
ASSERT(!node->m_next);
if (node->m_expr->isString()) {
const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
generator.emitThrowRangeError(ident);
} else {
RefPtr<RegisterID> message = generator.emitNode(node);
generator.emitThrowStaticError(ErrorType::RangeError, message.get());
}

generator.emitThrowRangeError(ident);
return dst;
}

@@ -4660,6 +4660,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
LAST_OPCODE(op_throw);

case op_throw_static_error:
addToGraph(Phantom, get(VirtualRegister(currentInstruction[1].u.operand))); // Keep argument live.
addToGraph(ThrowStaticError);
flushForTerminal();
addToGraph(Unreachable);
@@ -1,3 +1,54 @@
2016-10-06 Yusuke Suzuki <utatane.tea@gmail.com>

[WebCore][JSC] Use new @throwTypeError and @throwRangeError intrinsics
https://bugs.webkit.org/show_bug.cgi?id=163001

Reviewed by Keith Miller.

Replace `throw new @XXXError(...)` to @throwXXXError intrinsic.
It reduces the size of bytecode sequence and facilitate inlining.

No behavior change.

* Modules/fetch/FetchHeaders.js:
(initializeFetchHeaders):
* Modules/fetch/FetchInternals.js:
(fillFetchHeaders):
* Modules/fetch/FetchRequest.js:
(initializeFetchRequest):
* Modules/fetch/FetchResponse.js:
(initializeFetchResponse):
(clone):
* Modules/mediastream/NavigatorUserMedia.js:
(webkitGetUserMedia):
* Modules/mediastream/RTCPeerConnection.js:
(initializeRTCPeerConnection):
(getLocalStreams):
(getStreamById):
(addStream):
* Modules/streams/ReadableStream.js:
(initializeReadableStream):
(getReader):
* Modules/streams/ReadableStreamDefaultController.js:
(enqueue):
(error):
(close):
* Modules/streams/ReadableStreamDefaultReader.js:
(releaseLock):
* Modules/streams/ReadableStreamInternals.js:
(privateInitializeReadableStreamDefaultReader):
(privateInitializeReadableStreamDefaultController):
(doStructuredClone):
(readableStreamError):
* Modules/streams/StreamInternals.js:
(validateAndNormalizeQueuingStrategy):
(enqueueValueWithSize):
* Modules/streams/WritableStream.js:
(initializeWritableStream):
(state):
* xml/XMLHttpRequest.js:
(response):

2016-10-06 John Wilander <wilander@apple.com>

Update Resource Load Statistics
@@ -33,7 +33,7 @@ function initializeFetchHeaders(headersInit)
return this;

if (!@isObject(headersInit))
throw new @TypeError("headersInit must be an object");
@throwTypeError("headersInit must be an object");

@fillFetchHeaders(this, headersInit);

@@ -39,7 +39,7 @@ function fillFetchHeaders(headers, headersInit)
for (let i = 0; i < headersInit.length; i++) {
let header = headersInit[i];
if (header.length !== 2)
throw new @TypeError("headersInit sequence items should contain two values");
@throwTypeError("headersInit sequence items should contain two values");
@Headers.prototype.@appendFromJS.@call(headers, header[0], header[1]);
}
return this;
@@ -32,7 +32,7 @@ function initializeFetchRequest(input, init)
if (init === @undefined)
init = { };
else if (!@isObject(init))
throw new @TypeError("Request init must be an object");
@throwTypeError("Request init must be an object");

let headers = this.@initializeWith(input, init);
@assert(headers instanceof @Headers);
@@ -32,11 +32,11 @@ function initializeFetchResponse(body, init)
if (init === @undefined)
init = { };
else if (!@isObject(init))
throw new @TypeError("Response init must be an object");
@throwTypeError("Response init must be an object");

let status = (init.status !== @undefined) ? @toNumber(init.status) : 200;
if (status < 200 || status > 599)
throw new @RangeError("Status must be between 200 and 599");
@throwRangeError("Status must be between 200 and 599");

let statusText = (init.statusText !== @undefined) ? init.statusText : "OK";

@@ -47,7 +47,7 @@ function initializeFetchResponse(body, init)

if (body !== @undefined && body !== null) {
if (status == 101 || status == 204 || status == 205 || status == 304)
throw new @TypeError("Response cannot have a body with the given status");
@throwTypeError("Response cannot have a body with the given status");

// FIXME: Use @isReadableStream once it is no longer guarded by READABLE_STREAM_API guard.
let isBodyReadableStream = (@isObject(body) && !!body.@readableStreamController);
@@ -95,7 +95,7 @@ function clone()
throw @makeThisTypeError("Response", "clone");

if (@Response.prototype.@isDisturbed.@call(this) || (this.@body && @isReadableStreamLocked(this.@body)))
throw new @TypeError("Cannot clone a disturbed Response");
@throwTypeError("Cannot clone a disturbed Response");

var cloned = @Response.prototype.@cloneForJS.@call(this);

@@ -34,15 +34,15 @@ function webkitGetUserMedia(options, successCallback, errorCallback)
throw @makeThisTypeError("Navigator", "webkitGetUserMedia");

if (arguments.length < 3)
throw new @TypeError("Not enough arguments");
@throwTypeError("Not enough arguments");

if (options !== @Object(options))
throw new @TypeError("Argument 1 (options) to Navigator.webkitGetUserMedia must be an object");
@throwTypeError("Argument 1 (options) to Navigator.webkitGetUserMedia must be an object");

if (typeof successCallback !== "function")
throw new @TypeError("Argument 2 ('successCallback') to Navigator.webkitGetUserMedia must be a function");
@throwTypeError("Argument 2 ('successCallback') to Navigator.webkitGetUserMedia must be a function");
if (typeof errorCallback !== "function")
throw new @TypeError("Argument 3 ('errorCallback') to Navigator.webkitGetUserMedia must be a function");
@throwTypeError("Argument 3 ('errorCallback') to Navigator.webkitGetUserMedia must be a function");

this.mediaDevices.@getUserMedia(options).@then(successCallback, errorCallback);
}
@@ -35,18 +35,18 @@ function initializeRTCPeerConnection(configuration)
"use strict";

if (arguments.length < 1)
throw new @TypeError("Not enough arguments");
@throwTypeError("Not enough arguments");

if (!@isObject(configuration))
throw new @TypeError("RTCPeerConnection argument must be a valid Dictionary");
@throwTypeError("RTCPeerConnection argument must be a valid Dictionary");

// FIXME: Handle errors in a better way than catching and re-throwing (http://webkit.org/b/158936)
try {
this.@initializeWith(configuration);
} catch (e) {
const message = e.name === "TypeMismatchError" ? "Invalid RTCPeerConnection constructor arguments"
: "Error creating RTCPeerConnection";
throw new @TypeError(message);
@throwTypeError(message);
}

this.@operations = [];
@@ -60,7 +60,7 @@ function getLocalStreams()
"use strict";

if (!@isRTCPeerConnection(this))
throw new @TypeError("Function should be called on an RTCPeerConnection");
@throwTypeError("Function should be called on an RTCPeerConnection");

return this.@localStreams.slice();
}
@@ -70,10 +70,10 @@ function getStreamById(streamIdArg)
"use strict";

if (!@isRTCPeerConnection(this))
throw new @TypeError("Function should be called on an RTCPeerConnection");
@throwTypeError("Function should be called on an RTCPeerConnection");

if (arguments.length < 1)
throw new @TypeError("Not enough arguments");
@throwTypeError("Not enough arguments");

const streamId = @String(streamIdArg);

@@ -87,13 +87,13 @@ function addStream(stream)
"use strict";

if (!@isRTCPeerConnection(this))
throw new @TypeError("Function should be called on an RTCPeerConnection");
@throwTypeError("Function should be called on an RTCPeerConnection");

if (arguments.length < 1)
throw new @TypeError("Not enough arguments");
@throwTypeError("Not enough arguments");

if (!(stream instanceof @MediaStream))
throw new @TypeError("Argument 1 ('stream') to RTCPeerConnection.addStream must be an instance of MediaStream");
@throwTypeError("Argument 1 ('stream') to RTCPeerConnection.addStream must be an instance of MediaStream");

if (this.@localStreams.find(localStream => localStream.id === stream.id))
return;
@@ -107,13 +107,13 @@ function removeStream(stream)
"use strict";

if (!@isRTCPeerConnection(this))
throw new @TypeError("Function should be called on an RTCPeerConnection");
@throwTypeError("Function should be called on an RTCPeerConnection");

if (arguments.length < 1)
throw new @TypeError("Not enough arguments");
@throwTypeError("Not enough arguments");

if (!(stream instanceof @MediaStream))
throw new @TypeError("Argument 1 ('stream') to RTCPeerConnection.removeStream must be an instance of MediaStream");
@throwTypeError("Argument 1 ('stream') to RTCPeerConnection.removeStream must be an instance of MediaStream");

const indexOfStreamToRemove = this.@localStreams.findIndex(localStream => localStream.id === stream.id);
if (indexOfStreamToRemove === -1)
@@ -36,10 +36,10 @@ function initializeReadableStream(underlyingSource, strategy)
strategy = { highWaterMark: 1, size: function() { return 1; } };

if (!@isObject(underlyingSource))
throw new @TypeError("ReadableStream constructor takes an object as first argument");
@throwTypeError("ReadableStream constructor takes an object as first argument");

if (strategy !== @undefined && !@isObject(strategy))
throw new @TypeError("ReadableStream constructor takes an object as second argument, if any");
@throwTypeError("ReadableStream constructor takes an object as second argument, if any");

this.@state = @streamReadable;
this.@reader = @undefined;
@@ -53,13 +53,13 @@ function initializeReadableStream(underlyingSource, strategy)

if (typeString === "bytes") {
// FIXME: Implement support of ReadableByteStreamController.
throw new @TypeError("ReadableByteStreamController is not implemented");
@throwTypeError("ReadableByteStreamController is not implemented");
} else if (type === @undefined) {
if (strategy.highWaterMark === @undefined)
strategy.highWaterMark = 1;
this.@readableStreamController = new @ReadableStreamDefaultController(this, underlyingSource, strategy.size, strategy.highWaterMark);
} else
throw new @RangeError("Invalid type for underlying source");
@throwRangeError("Invalid type for underlying source");

return this;
}
@@ -89,13 +89,13 @@ function getReader(options)

if (options.mode === 'byob') {
// FIXME: Update once ReadableByteStreamContoller and ReadableStreamBYOBReader are implemented.
throw new @TypeError("ReadableStreamBYOBReader is not implemented");
@throwTypeError("ReadableStreamBYOBReader is not implemented");
}

if (options.mode === @undefined)
return new @ReadableStreamDefaultReader(this);

throw new @RangeError("Invalid mode is specified");
@throwRangeError("Invalid mode is specified");
}

function pipeThrough(streams, options)
@@ -33,10 +33,10 @@ function enqueue(chunk)
throw @makeThisTypeError("ReadableStreamDefaultController", "enqueue");

if (this.@closeRequested)
throw new @TypeError("ReadableStreamDefaultController is requested to close");
@throwTypeError("ReadableStreamDefaultController is requested to close");

if (this.@controlledReadableStream.@state !== @streamReadable)
throw new @TypeError("ReadableStream is not readable");
@throwTypeError("ReadableStream is not readable");

return @readableStreamDefaultControllerEnqueue(this, chunk);
}
@@ -50,7 +50,7 @@ function error(error)

const stream = this.@controlledReadableStream;
if (stream.@state !== @streamReadable)
throw new @TypeError("ReadableStream is not readable");
@throwTypeError("ReadableStream is not readable");

@readableStreamError(stream, error);
}
@@ -63,10 +63,10 @@ function close()
throw @makeThisTypeError("ReadableStreamDefaultController", "close");

if (this.@closeRequested)
throw new @TypeError("ReadableStreamDefaultController is already requested to close");
@throwTypeError("ReadableStreamDefaultController is already requested to close");

if (this.@controlledReadableStream.@state !== @streamReadable)
throw new @TypeError("ReadableStream is not readable");
@throwTypeError("ReadableStream is not readable");

@readableStreamDefaultControllerClose(this);
}
@@ -62,7 +62,7 @@ function releaseLock()
return;

if (this.@readRequests.length)
throw new @TypeError("There are still pending read requests, cannot release the lock");
@throwTypeError("There are still pending read requests, cannot release the lock");

if (stream.@state === @streamReadable)
this.@closedPromiseCapability.@reject.@call(@undefined, new @TypeError("releasing lock of reader whose stream is still in readable state"));

0 comments on commit 30070a4

Please sign in to comment.