Bug 930414 - Add ModuleObject and CompileModule() function r=shu

This commit is contained in:
Jon Coppeard 2015-08-24 15:58:35 +01:00
Родитель 0ecb01460a
Коммит adf4b1ae87
24 изменённых файлов: 556 добавлений и 26 удалений

Просмотреть файл

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/ModuleObject.h"
#include "jsobjinlines.h"
using namespace js;
///////////////////////////////////////////////////////////////////////////
// ModuleObject
const Class ModuleObject::class_ = {
"Module",
JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
JSCLASS_IS_ANONYMOUS |
JSCLASS_IMPLEMENTS_BARRIERS,
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
nullptr, /* convert */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
ModuleObject::trace
};
/* static */ ModuleObject*
ModuleObject::create(ExclusiveContext* cx)
{
return NewBuiltinClassInstance<ModuleObject>(cx, TenuredObject);
}
void
ModuleObject::init(HandleScript script)
{
initReservedSlot(ScriptSlot, PrivateValue(script));
}
JSScript*
ModuleObject::script() const
{
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
}
/* static */ void
ModuleObject::trace(JSTracer* trc, JSObject* obj)
{
ModuleObject& module = obj->as<ModuleObject>();
JSScript* script = module.script();
TraceManuallyBarrieredEdge(trc, &script, "Module script");
module.setReservedSlot(ScriptSlot, PrivateValue(script));
}

Просмотреть файл

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef builtin_ModuleObject_h
#define builtin_ModuleObject_h
#include "vm/NativeObject.h"
namespace js {
class ModuleObject : public NativeObject
{
public:
enum
{
ScriptSlot = 0,
SlotCount
};
static const Class class_;
static ModuleObject* create(ExclusiveContext* cx);
void init(HandleScript script);
JSScript* script() const;
private:
static void trace(JSTracer* trc, JSObject* obj);
};
typedef Rooted<ModuleObject*> RootedModuleObject;
typedef Handle<ModuleObject*> HandleModuleObject;
} // namespace js
#endif /* builtin_ModuleObject_h */

Просмотреть файл

@ -10,6 +10,7 @@
#include "jsscript.h"
#include "asmjs/AsmJSLink.h"
#include "builtin/ModuleObject.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/FoldConstants.h"
#include "frontend/NameFunctions.h"
@ -57,6 +58,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
void setSourceArgumentsNotIncluded();
JSScript* compileScript(HandleObject scopeChain, HandleScript evalCaller);
ModuleObject* compileModule();
bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
GeneratorKind generatorKind);
@ -606,6 +608,50 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller
return script;
}
ModuleObject* BytecodeCompiler::compileModule()
{
MOZ_ASSERT(!enclosingStaticScope);
if (!createSourceAndParser())
return nullptr;
if (!createScript())
return nullptr;
Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
if (!module)
return nullptr;
module->init(script);
ParseNode* pn = parser->standaloneModule(module);
if (!pn)
return nullptr;
if (!NameFunctions(cx, pn) ||
!maybeSetDisplayURL(parser->tokenStream) ||
!maybeSetSourceMap(parser->tokenStream))
{
return nullptr;
}
script->bindings = pn->pn_modulebox->bindings;
if (!createEmitter(pn->pn_modulebox) ||
!emitter->emitModuleScript(pn->pn_body))
{
return nullptr;
}
if (!maybeCompleteCompressSource())
return nullptr;
parser->handler.freeTree(pn);
MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
return module;
}
bool
BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
Handle<PropertyNameVector> formals,
@ -721,6 +767,22 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
return compiler.compileScript(scopeChain, evalCaller);
}
ModuleObject*
frontend::CompileModule(JSContext* cx, HandleObject obj,
const ReadOnlyCompileOptions& optionsInput,
SourceBufferHolder& srcBuf)
{
MOZ_ASSERT(srcBuf.get());
CompileOptions options(cx, optionsInput);
options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
options.setIsRunOnce(true);
BytecodeCompiler compiler(cx, &cx->tempLifoAlloc(), options, srcBuf, nullptr,
TraceLogger_ParserCompileModule);
return compiler.compileModule();
}
bool
frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
{

Просмотреть файл

@ -17,6 +17,7 @@ namespace js {
class LazyScript;
class LifoAlloc;
class ModuleObject;
class ScriptSourceObject;
class ScopeObject;
struct SourceCompressionTask;
@ -30,6 +31,10 @@ CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
SourceCompressionTask* extraSct = nullptr);
ModuleObject *
CompileModule(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
SourceBufferHolder &srcBuf);
bool
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);

Просмотреть файл

@ -2212,6 +2212,10 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
*answer = false;
return true;
case PNK_MODULE:
*answer = false;
return true;
// Generator expressions have no side effects on their own.
case PNK_GENEXP:
MOZ_ASSERT(pn->isArity(PN_LIST));
@ -3500,6 +3504,55 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
return true;
}
bool
BytecodeEmitter::emitModuleScript(ParseNode* body)
{
if (!updateLocalsToFrameSlots())
return false;
/*
* IonBuilder has assumptions about what may occur immediately after
* script->main (e.g., in the case of destructuring params). Thus, put the
* following ops into the range [script->code, script->main). Note:
* execution starts from script->code, so this has no semantic effect.
*/
ModuleBox* modulebox = sc->asModuleBox();
// Link the module and the script to each other, so that StaticScopeIter
// may walk the scope chain of currently compiling scripts.
JSScript::linkToModuleFromEmitter(cx, script, modulebox);
if (!emitTree(body))
return false;
// Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
// depend on this opcode, e.g. InterpreterRegs::setToEndOfScript.
if (!emit1(JSOP_RETRVAL))
return false;
// If all locals are aliased, the frame's block slots won't be used, so we
// can set numBlockScoped = 0. This is nice for generators as it ensures
// nfixed == 0, so we don't have to initialize any local slots when resuming
// a generator.
if (sc->allLocalsAliased())
script->bindings.setAllLocalsAliased();
if (!JSScript::fullyInitFromEmitter(cx, script, this))
return false;
/*
* Since modules are only run once. Mark the script so that initializers
* created within it may be given more precise types.
*/
script->setTreatAsRunOnce();
MOZ_ASSERT(!script->hasRunOnce());
tellDebuggerAboutCompiledScript(cx);
return true;
}
bool
BytecodeEmitter::maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* result)
{

Просмотреть файл

@ -320,6 +320,9 @@ struct BytecodeEmitter
// Emit function code for the tree rooted at body.
bool emitFunctionScript(ParseNode* body);
// Emit module code for the tree rooted at body.
bool emitModuleScript(ParseNode* body);
// If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
// reserve a type set to store its result.
void checkTypeSet(JSOp op);

Просмотреть файл

@ -100,6 +100,10 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
*result = false;
return true;
case PNK_MODULE:
*result = false;
return true;
// Statements with no sub-components at all.
case PNK_NOP: // induced by function f() {} function f() {}
case PNK_DEBUGGER:
@ -1114,6 +1118,17 @@ ComputeBinary(ParseNodeKind kind, double left, double right)
return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
}
static bool
FoldModule(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
{
MOZ_ASSERT(node->isKind(PNK_MODULE));
MOZ_ASSERT(node->isArity(PN_CODE));
ParseNode*& moduleBody = node->pn_body;
MOZ_ASSERT(moduleBody);
return Fold(cx, &moduleBody, parser, false);
}
static bool
FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
bool inGenexpLambda)
@ -1787,6 +1802,9 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
case PNK_FUNCTION:
return FoldFunction(cx, pn, parser, inGenexpLambda);
case PNK_MODULE:
return FoldModule(cx, pn, parser);
case PNK_SUB:
case PNK_STAR:
case PNK_LSH:

Просмотреть файл

@ -635,7 +635,7 @@ class FullParseHandler
inline void setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* pn);
ParseNode* newFunctionDefinition() {
return new_<CodeNode>(pos());
return new_<CodeNode>(PNK_FUNCTION, pos());
}
void setFunctionBody(ParseNode* pn, ParseNode* kid) {
pn->pn_body = kid;
@ -652,6 +652,14 @@ class FullParseHandler
pn->pn_funbox->setDerivedClassConstructor();
}
ParseNode* newModule() {
return new_<CodeNode>(PNK_MODULE, pos());
}
void setModuleBox(ParseNode* pn, ModuleBox* modulebox) {
MOZ_ASSERT(pn->isKind(PNK_MODULE));
pn->pn_modulebox = modulebox;
}
ParseNode* newLexicalScope(ObjectBox* blockBox) {
return new_<LexicalScopeNode>(blockBox, pos());
}

Просмотреть файл

@ -337,7 +337,7 @@ class NameResolver
if (cur == nullptr)
return true;
MOZ_ASSERT(cur->isKind(PNK_FUNCTION) == cur->isArity(PN_CODE));
MOZ_ASSERT((cur->isKind(PNK_FUNCTION) || cur->isKind(PNK_MODULE)) == cur->isArity(PN_CODE));
if (cur->isKind(PNK_FUNCTION)) {
RootedAtom prefix2(cx);
if (!resolveFun(cur, prefix, &prefix2))
@ -767,6 +767,7 @@ class NameResolver
break;
case PNK_FUNCTION:
case PNK_MODULE:
MOZ_ASSERT(cur->isArity(PN_CODE));
if (!resolve(cur->pn_body, prefix))
return false;

Просмотреть файл

@ -521,6 +521,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
return PushNameNodeChildren(pn, stack);
case PNK_FUNCTION:
case PNK_MODULE:
return PushCodeNodeChildren(pn, stack);
case PNK_LIMIT: // invalid sentinel value
@ -1151,6 +1152,13 @@ ObjectBox::asFunctionBox()
return static_cast<FunctionBox*>(this);
}
ModuleBox*
ObjectBox::asModuleBox()
{
MOZ_ASSERT(isModuleBox());
return static_cast<ModuleBox*>(this);
}
void
ObjectBox::trace(JSTracer* trc)
{
@ -1162,6 +1170,9 @@ ObjectBox::trace(JSTracer* trc)
funbox->bindings.trace(trc);
if (funbox->enclosingStaticScope_)
TraceRoot(trc, &funbox->enclosingStaticScope_, "funbox-enclosingStaticScope");
} else if (box->isModuleBox()) {
ModuleBox* modulebox = box->asModuleBox();
modulebox->bindings.trace(trc);
}
box = box->traceLink;
}

Просмотреть файл

@ -19,6 +19,7 @@ struct ParseContext;
class FullParseHandler;
class FunctionBox;
class ModuleBox;
class ObjectBox;
// A packed ScopeCoordinate for use in the frontend during bytecode
@ -118,6 +119,7 @@ class PackedScopeCoordinate
F(NULL) \
F(THIS) \
F(FUNCTION) \
F(MODULE) \
F(IF) \
F(SWITCH) \
F(CASE) \
@ -636,9 +638,10 @@ class ParseNode
} unary;
struct { /* name, labeled statement, etc. */
union {
JSAtom* atom; /* lexical name or label atom */
ObjectBox* objbox; /* block or regexp object */
JSAtom* atom; /* lexical name or label atom */
ObjectBox* objbox; /* block or regexp object */
FunctionBox* funbox; /* function object */
ModuleBox* modulebox; /* module object */
};
union {
ParseNode* expr; /* module or function body, var
@ -662,6 +665,7 @@ class ParseNode
} pn_u;
#define pn_modulebox pn_u.name.modulebox
#define pn_objbox pn_u.name.objbox
#define pn_funbox pn_u.name.funbox
#define pn_body pn_u.name.expr
#define pn_scopecoord pn_u.name.scopeCoord
@ -1072,15 +1076,17 @@ struct ListNode : public ParseNode
struct CodeNode : public ParseNode
{
explicit CodeNode(const TokenPos& pos)
: ParseNode(PNK_FUNCTION, JSOP_NOP, PN_CODE, pos)
CodeNode(ParseNodeKind kind, const TokenPos& pos)
: ParseNode(kind, JSOP_NOP, PN_CODE, pos)
{
MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE);
MOZ_ASSERT(!pn_body);
MOZ_ASSERT(!pn_funbox);
MOZ_ASSERT(!pn_objbox);
MOZ_ASSERT(pn_dflags == 0);
pn_scopecoord.makeFree();
}
public:
#ifdef DEBUG
void dump(int indent);
#endif
@ -1687,6 +1693,8 @@ class ObjectBox
ObjectBox(JSObject* object, ObjectBox* traceLink);
bool isFunctionBox() { return object->is<JSFunction>(); }
FunctionBox* asFunctionBox();
bool isModuleBox() { return object->is<ModuleObject>(); }
ModuleBox* asModuleBox();
void trace(JSTracer* trc);
protected:

Просмотреть файл

@ -29,6 +29,7 @@
#include "jstypes.h"
#include "asmjs/AsmJSValidate.h"
#include "builtin/ModuleObject.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FoldConstants.h"
#include "frontend/ParseMaps.h"
@ -409,12 +410,9 @@ AppendPackedBindings(const ParseContext<ParseHandler>* pc, const DeclVector& vec
template <typename ParseHandler>
bool
ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
{
MOZ_ASSERT(sc->isFunctionBox());
MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
/*
@ -453,6 +451,28 @@ ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, Token
packedBindings);
}
template <typename ParseHandler>
bool
ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
{
MOZ_ASSERT(sc->isFunctionBox());
MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
return generateBindings(cx, ts, alloc, bindings);
}
template <typename ParseHandler>
bool
ParseContext<ParseHandler>::generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
{
MOZ_ASSERT(sc->isModuleBox());
MOZ_ASSERT(args_.length() == 0);
return generateBindings(cx, ts, alloc, bindings);
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
@ -688,6 +708,49 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
return funbox;
}
template <typename ParseHandler>
ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
ParseContext<ParseHandler>* outerpc)
: ObjectBox(module, traceListHead),
SharedContext(cx, Directives(true), extraWarnings),
bindings()
{}
template <typename ParseHandler>
ModuleBox*
Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
{
MOZ_ASSERT(module);
/*
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
* on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
* arenas containing the entries must be alive until we are done with
* scanning, parsing and code generation for the whole module.
*/
ParseContext<ParseHandler>* outerpc = nullptr;
ModuleBox* modbox =
alloc.new_<ModuleBox>(context, traceListHead, module, outerpc);
if (!modbox) {
ReportOutOfMemory(context);
return nullptr;
}
traceListHead = modbox;
if (pn)
handler.setModuleBox(pn, modbox);
return modbox;
}
template <>
ModuleBox*
Parser<SyntaxParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
{
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return nullptr;
}
template <typename ParseHandler>
void
Parser<ParseHandler>::trace(JSTracer* trc)
@ -785,6 +848,57 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
{
MOZ_ASSERT(checkOptionsCalled);
Node mn = handler.newModule();
if (!mn)
return null();
ModuleBox* modulebox = newModuleBox(mn, module);
if (!modulebox)
return null();
handler.setModuleBox(mn, modulebox);
ParseContext<FullParseHandler> modulepc(this, pc, mn, modulebox, nullptr, 0);
if (!modulepc.init(*this))
return null();
ParseNode* pn = statements(YieldIsKeyword);
if (!pn)
return null();
MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
mn->pn_body = pn;
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
MOZ_ASSERT(tt == TOK_EOF);
if (!FoldConstants(context, &pn, this))
return null();
Rooted<Bindings> bindings(context, modulebox->bindings);
if (!modulepc.generateModuleBindings(context, tokenStream, alloc, &bindings))
return null();
modulebox->bindings = bindings;
MOZ_ASSERT(mn->pn_modulebox == modulebox);
return mn;
}
template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module)
{
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
}
template <>
ParseNode*
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,

Просмотреть файл

@ -22,7 +22,9 @@
namespace js {
class ModuleObject;
class StaticFunctionBoxScopeObject;
class StaticModuleBoxScopeObject;
namespace frontend {
@ -197,23 +199,32 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
void updateDecl(JSAtom* atom, Node newDecl);
/*
* After a function body has been parsed, the parser generates the
* function's "bindings". Bindings are a data-structure, ultimately stored
* in the compiled JSScript, that serve three purposes:
* After a function body or module has been parsed, the parser generates the
* code's "bindings". Bindings are a data-structure, ultimately stored in
* the compiled JSScript, that serve three purposes:
* - After parsing, the ParseContext is destroyed and 'decls' along with
* it. Mostly, the emitter just uses the binding information stored in
* the use/def nodes, but the emitter occasionally needs 'bindings' for
* various scope-related queries.
* - Bindings provide the initial js::Shape to use when creating a dynamic
* scope object (js::CallObject) for the function. This shape is used
* during dynamic name lookup.
* scope object (js::CallObject). This shape is used during dynamic name
* lookup.
* - Sometimes a script's bindings are accessed at runtime to retrieve the
* contents of the lexical scope (e.g., from the debugger).
*/
private:
bool generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const;
public:
bool generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const;
bool generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const;
private:
ParseContext** parserPC; /* this points to the Parser's active pc
and holds either |this| or one of
@ -300,8 +311,13 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
// function f1() { function f2() { } }
// if (cond) { function f3() { if (cond) { function f4() { } } } }
//
bool atBodyLevel() { return !innermostStmt(); }
bool atGlobalLevel() { return atBodyLevel() && !sc->isFunctionBox() && !innermostScopeStmt(); }
bool atBodyLevel() {
return !innermostStmt();
}
bool atGlobalLevel() {
return atBodyLevel() && !sc->isFunctionBox() && !innermostScopeStmt();
}
// True if this is the ParseContext for the body of a function created by
// the Function constructor.
@ -490,6 +506,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
}
ModuleBox* newModuleBox(Node pn, HandleModuleObject module);
/*
* Create a new function object given a name (which is optional if this is
* a function expression).
@ -542,6 +560,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
bool maybeParseDirective(Node list, Node pn, bool* cont);
Node standaloneModule(Handle<ModuleObject*> module);
// Parse a function, given only its body. Used for the Function and
// Generator constructors.
Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,

Просмотреть файл

@ -13,6 +13,7 @@
#include "jsscript.h"
#include "jstypes.h"
#include "builtin/ModuleObject.h"
#include "frontend/ParseMaps.h"
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
@ -215,8 +216,11 @@ class SharedContext
void computeInWith(JSObject* staticScope);
virtual ObjectBox* toObjectBox() { return nullptr; }
inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
bool isObjectBox() { return toObjectBox() != nullptr; }
bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
inline FunctionBox* asFunctionBox();
bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); }
inline ModuleBox* asModuleBox();
bool allowNewTarget() const { return allowNewTarget_; }
bool allowSuperProperty() const { return allowSuperProperty_; }
@ -368,6 +372,20 @@ class FunctionBox : public ObjectBox, public SharedContext
}
};
class ModuleBox : public ObjectBox, public SharedContext
{
public:
Bindings bindings;
template <typename ParseHandler>
ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
ParseContext<ParseHandler>* pc);
ObjectBox* toObjectBox() override { return this; }
ModuleObject* module() const { return &object->as<ModuleObject>(); }
JSObject* staticScope() const override { return module(); }
};
inline FunctionBox*
SharedContext::asFunctionBox()
{
@ -375,6 +393,13 @@ SharedContext::asFunctionBox()
return static_cast<FunctionBox*>(this);
}
inline ModuleBox*
SharedContext::asModuleBox()
{
MOZ_ASSERT(isModuleBox());
return static_cast<ModuleBox*>(this);
}
// In generators, we treat all locals as aliased so that they get stored on the
// heap. This way there is less information to copy off the stack when

Просмотреть файл

@ -361,6 +361,7 @@ AssertRootMarkingPhase(JSTracer* trc)
D(GlobalObject*) \
D(JSObject*) \
D(JSFunction*) \
D(ModuleObject*) \
D(NestedScopeObject*) \
D(PlainObject*) \
D(SavedFrame*) \

Просмотреть файл

@ -0,0 +1,4 @@
// Exercise shell parseModule function.
parseModule("");
parseModule("const foo = 1;");

Просмотреть файл

@ -2652,6 +2652,25 @@ JSScript::linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScrip
fun->setScript(script);
}
/* static */ void
JSScript::linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
js::frontend::ModuleBox* modulebox)
{
script->funHasExtensibleScope_ = false;
script->funNeedsDeclEnvObject_ = false;
script->needsHomeObject_ = false;
script->isDerivedClassConstructor_ = false;
script->funLength_ = 0;
script->isGeneratorExp_ = false;
script->setGeneratorKind(NotGenerator);
// Link the module and the script to each other, so that StaticScopeIter
// may walk the scope chain of currently compiling scripts.
RootedModuleObject module(cx, modulebox->module());
script->setModule(module);
}
/* static */ bool
JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, BytecodeEmitter* bce)
{
@ -3595,6 +3614,9 @@ JSScript::traceChildren(JSTracer* trc)
if (functionNonDelazifying())
TraceEdge(trc, &function_, "function");
if (module_)
TraceEdge(trc, &module_, "module");
if (enclosingStaticScope_)
TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope");

Просмотреть файл

@ -18,6 +18,7 @@
#include "jsopcode.h"
#include "jstypes.h"
#include "builtin/ModuleObject.h"
#include "gc/Barrier.h"
#include "gc/Rooting.h"
#include "jit/IonCode.h"
@ -46,15 +47,16 @@ class BreakpointSite;
class BindingIter;
class Debugger;
class LazyScript;
class NestedScopeObject;
class RegExpObject;
struct SourceCompressionTask;
class Shape;
class NestedScopeObject;
namespace frontend {
struct BytecodeEmitter;
class UpvarCookie;
class FunctionBox;
class ModuleBox;
} // namespace frontend
namespace detail {
@ -949,6 +951,7 @@ class JSScript : public js::gc::TenuredCell
js::HeapPtrObject sourceObject_;
js::HeapPtrFunction function_;
js::HeapPtr<js::ModuleObject*> module_;
js::HeapPtrObject enclosingStaticScope_;
/*
@ -1139,7 +1142,7 @@ class JSScript : public js::gc::TenuredCell
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
uint32_t padding;
// No padding currently required.
#endif
//
@ -1167,6 +1170,8 @@ class JSScript : public js::gc::TenuredCell
js::frontend::BytecodeEmitter* bce);
static void linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
js::frontend::FunctionBox* funbox);
static void linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
js::frontend::ModuleBox* funbox);
// Initialize a no-op script.
static bool fullyInitTrivial(js::ExclusiveContext* cx, JS::Handle<JSScript*> script);
@ -1547,6 +1552,11 @@ class JSScript : public js::gc::TenuredCell
*/
inline void ensureNonLazyCanonicalFunction(JSContext* cx);
js::ModuleObject* module() const {
return module_;
}
inline void setModule(js::ModuleObject* module);
JSFlatString* sourceData(JSContext* cx);
static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);

Просмотреть файл

@ -67,10 +67,18 @@ JSScript::functionDelazifying() const
inline void
JSScript::setFunction(JSFunction* fun)
{
MOZ_ASSERT(!function_ && !module_);
MOZ_ASSERT(fun->isTenured());
function_ = fun;
}
inline void
JSScript::setModule(js::ModuleObject* module)
{
MOZ_ASSERT(!function_ && !module_);
module_ = module;
}
inline void
JSScript::ensureNonLazyCanonicalFunction(JSContext* cx)
{

Просмотреть файл

@ -147,6 +147,7 @@ UNIFIED_SOURCES += [
'builtin/Eval.cpp',
'builtin/Intl.cpp',
'builtin/MapObject.cpp',
'builtin/ModuleObject.cpp',
'builtin/Object.cpp',
'builtin/Profilers.cpp',
'builtin/Reflect.cpp',

Просмотреть файл

@ -56,6 +56,7 @@
#endif
#include "jswrapper.h"
#include "builtin/ModuleObject.h"
#include "builtin/TestingFunctions.h"
#include "frontend/Parser.h"
#include "gc/GCInternals.h"
@ -2067,7 +2068,12 @@ DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp, Sprinter* sprinte
} else {
for (unsigned i = 0; i < p.argc; i++) {
RootedFunction fun(cx);
RootedScript script (cx, ValueToScript(cx, p.argv[i], fun.address()));
RootedScript script(cx);
RootedValue value(cx, p.argv[i]);
if (value.isObject() && value.toObject().is<ModuleObject>())
script = value.toObject().as<ModuleObject>().script();
else
script = ValueToScript(cx, value, fun.address());
if (!script)
return false;
if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, sprinter))
@ -3058,6 +3064,45 @@ Compile(JSContext* cx, unsigned argc, Value* vp)
return ok;
}
static bool
ParseModule(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
JSMSG_MORE_ARGS_NEEDED, "parseModule", "0", "s");
return false;
}
if (!args[0].isString()) {
JS_ReportError(cx, "expected string to compile, got %s",
JS_TypeOfValue(cx, args[0]));
return false;
}
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx);
if (!scriptContents)
return false;
CompileOptions options(cx);
options.setFileAndLine("<string>", 1);
AutoStableStringChars stableChars(cx);
if (!stableChars.initTwoByte(cx, scriptContents))
return false;
const char16_t* chars = stableChars.twoByteRange().start().get();
SourceBufferHolder srcBuf(chars, scriptContents->length(),
SourceBufferHolder::NoOwnership);
RootedObject module(cx, frontend::CompileModule(cx, global, options, srcBuf));
if (!module)
return false;
args.rval().setObject(*module);
return true;
}
static bool
Parse(JSContext* cx, unsigned argc, Value* vp)
{
@ -4606,6 +4651,10 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
"compile(code)",
" Compiles a string to bytecode, potentially throwing."),
JS_FN_HELP("parseModule", ParseModule, 1, 0,
"parseModule(code)",
" Parses source text as a module and returns a Module object."),
JS_FN_HELP("parse", Parse, 1, 0,
"parse(code)",
" Parses a string, potentially throwing."),

Просмотреть файл

@ -21,6 +21,7 @@ namespace js {
namespace frontend {
struct Definition;
class FunctionBox;
class ModuleBox;
}
class StaticWithObject;
@ -47,6 +48,9 @@ class StaticNonSyntacticScopeObjects;
* JSFunction
* Scope for function bodies. e.g., |function f() { var x; let y; }|
*
* ModuleObject
* Scope for moddules.
*
* StaticWithObject
* Scope for |with|. e.g., |with ({}) { ... }|
*
@ -63,8 +67,8 @@ class StaticNonSyntacticScopeObjects;
*
* (function f() { var x; function g() { } })
*
* All static scope objects are ScopeObjects with the exception of
* JSFunction. A JSFunction keeps its enclosing scope link on
* All static scope objects are ScopeObjects with the exception of JSFunction
* and ModuleObject, which keeps their enclosing scope link on
* |JSScript::enclosingStaticScope()|.
*/
template <AllowGC allowGC>
@ -78,7 +82,8 @@ class StaticScopeIter
obj->is<StaticWithObject>() ||
obj->is<StaticEvalObject>() ||
obj->is<StaticNonSyntacticScopeObjects>() ||
obj->is<JSFunction>();
obj->is<JSFunction>() ||
obj->is<ModuleObject>();
}
public:

Просмотреть файл

@ -672,6 +672,7 @@ TraceLoggerThreadState::init()
enabledTextIds[TraceLogger_ParserCompileFunction] = true;
enabledTextIds[TraceLogger_ParserCompileLazy] = true;
enabledTextIds[TraceLogger_ParserCompileScript] = true;
enabledTextIds[TraceLogger_ParserCompileModule] = true;
enabledTextIds[TraceLogger_IrregexpCompile] = true;
enabledTextIds[TraceLogger_IrregexpExecute] = true;
enabledTextIds[TraceLogger_Scripts] = true;

Просмотреть файл

@ -32,6 +32,7 @@
_(ParserCompileFunction) \
_(ParserCompileLazy) \
_(ParserCompileScript) \
_(ParserCompileModule) \
_(Scripts) \
_(VM) \
\