Bug 965880 - OdinMonkey: allow typed array constructors to be imported and used (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Tue, 07 Oct 2014 14:07:49 -0500
changeset 209233 09bd9d93d3e2574abdc5db3f608737a309eb8d59
parent 209232 c5e310d17e58610b6f1b1b13779a98a1ccc1acb4
child 209234 f72b6d7ece75f3824d6322dc011a41ef5c049c2d
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbbouvier
bugs965880
milestone35.0a1
Bug 965880 - OdinMonkey: allow typed array constructors to be imported and used (r=bbouvier)
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/jit-test/tests/asm.js/testResize.js
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -214,20 +214,22 @@ ValidateFFI(JSContext *cx, AsmJSModule::
     if (!v.isObject() || !v.toObject().is<JSFunction>())
         return LinkFail(cx, "FFI imports must be functions");
 
     (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
     return true;
 }
 
 static bool
-ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
-                  HandleValue bufferVal)
+ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
 {
-    RootedPropertyName field(cx, global.viewName());
+    RootedPropertyName field(cx, global.maybeViewName());
+    if (!field)
+        return true;
+
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, field, &v))
         return false;
 
     if (!IsTypedArrayConstructor(v, global.viewType()) &&
         !IsSharedTypedArrayConstructor(v, global.viewType()))
     {
         return LinkFail(cx, "bad typed array constructor");
@@ -503,17 +505,18 @@ DynamicallyLinkModule(JSContext *cx, Cal
             if (!ValidateGlobalVariable(cx, module, global, importVal))
                 return false;
             break;
           case AsmJSModule::Global::FFI:
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
           case AsmJSModule::Global::ArrayView:
-            if (!ValidateArrayView(cx, global, globalVal, bufferVal))
+          case AsmJSModule::Global::ArrayViewCtor:
+            if (!ValidateArrayView(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::MathBuiltinFunction:
             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::Constant:
             if (!ValidateConstant(cx, global, globalVal))
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -195,17 +195,17 @@ class AsmJSNumLit
 //
 // NB: this means that AsmJSModule must be GC-safe.
 class AsmJSModule
 {
   public:
     class Global
     {
       public:
-        enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant,
+        enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, Constant,
                      SimdCtor, SimdOperation};
         enum VarInitKind { InitConstant, InitImport };
         enum ConstantKind { GlobalConstant, MathConstant };
 
       private:
         struct Pod {
             Which which_;
             union {
@@ -279,22 +279,26 @@ class AsmJSModule
         PropertyName *ffiField() const {
             MOZ_ASSERT(pod.which_ == FFI);
             return name_;
         }
         uint32_t ffiIndex() const {
             MOZ_ASSERT(pod.which_ == FFI);
             return pod.u.ffiIndex_;
         }
-        PropertyName *viewName() const {
-            MOZ_ASSERT(pod.which_ == ArrayView);
+        // When a view is created from an imported constructor:
+        //   var I32 = stdlib.Int32Array;
+        //   var i32 = new I32(buffer);
+        // the second import has nothing to validate and thus has a null field.
+        PropertyName *maybeViewName() const {
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
             return name_;
         }
         Scalar::Type viewType() const {
-            MOZ_ASSERT(pod.which_ == ArrayView);
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
             return pod.u.viewType_;
         }
         PropertyName *mathName() const {
             MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
             return name_;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
@@ -938,20 +942,27 @@ class AsmJSModule
     bool addFFI(PropertyName *field, uint32_t *ffiIndex) {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         if (pod.numFFIs_ == UINT32_MAX)
             return false;
         Global g(Global::FFI, field);
         g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
         return globals_.append(g);
     }
-    bool addArrayView(Scalar::Type vt, PropertyName *field) {
+    bool addArrayView(Scalar::Type vt, PropertyName *maybeField) {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         pod.hasArrayView_ = true;
-        Global g(Global::ArrayView, field);
+        Global g(Global::ArrayView, maybeField);
+        g.pod.u.viewType_ = vt;
+        return globals_.append(g);
+    }
+    bool addArrayViewCtor(Scalar::Type vt, PropertyName *field) {
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(field);
+        Global g(Global::ArrayViewCtor, field);
         g.pod.u.viewType_ = vt;
         return globals_.append(g);
     }
     bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::MathBuiltinFunction, field);
         g.pod.u.mathBuiltinFunc_ = func;
         return globals_.append(g);
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1081,16 +1081,17 @@ class MOZ_STACK_CLASS ModuleCompiler
         enum Which {
             Variable,
             ConstantLiteral,
             ConstantImport,
             Function,
             FuncPtrTable,
             FFI,
             ArrayView,
+            ArrayViewCtor,
             MathBuiltinFunction,
             SimdCtor,
             SimdOperation
         };
 
       private:
         Which which_;
         union {
@@ -1143,17 +1144,17 @@ class MOZ_STACK_CLASS ModuleCompiler
             MOZ_ASSERT(which_ == FuncPtrTable);
             return u.funcPtrTableIndex_;
         }
         unsigned ffiIndex() const {
             MOZ_ASSERT(which_ == FFI);
             return u.ffiIndex_;
         }
         Scalar::Type viewType() const {
-            MOZ_ASSERT(which_ == ArrayView);
+            MOZ_ASSERT(which_ == ArrayView || which_ == ArrayViewCtor);
             return u.viewType_;
         }
         bool isMathFunction() const {
             return which_ == MathBuiltinFunction;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             MOZ_ASSERT(which_ == MathBuiltinFunction);
             return u.mathBuiltinFunc_;
@@ -1618,21 +1619,30 @@ class MOZ_STACK_CLASS ModuleCompiler
         if (!global)
             return false;
         uint32_t index;
         if (!module_->addFFI(field, &index))
             return false;
         global->u.ffiIndex_ = index;
         return globals_.putNew(varName, global);
     }
-    bool addArrayView(PropertyName *varName, Scalar::Type vt, PropertyName *fieldName) {
+    bool addArrayView(PropertyName *varName, Scalar::Type vt, PropertyName *maybeField) {
         Global *global = moduleLifo_.new_<Global>(Global::ArrayView);
         if (!global)
             return false;
-        if (!module_->addArrayView(vt, fieldName))
+        if (!module_->addArrayView(vt, maybeField))
+            return false;
+        global->u.viewType_ = vt;
+        return globals_.putNew(varName, global);
+    }
+    bool addArrayViewCtor(PropertyName *varName, Scalar::Type vt, PropertyName *fieldName) {
+        Global *global = moduleLifo_.new_<Global>(Global::ArrayViewCtor);
+        if (!global)
+            return false;
+        if (!module_->addArrayViewCtor(vt, fieldName))
             return false;
         global->u.viewType_ = vt;
         return globals_.putNew(varName, global);
     }
     bool addMathBuiltinFunction(PropertyName *varName, AsmJSMathBuiltinFunction func, PropertyName *fieldName) {
         if (!module_->addMathBuiltinFunction(func, fieldName))
             return false;
         Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction);
@@ -3495,62 +3505,87 @@ CheckGlobalVariableInitImport(ModuleComp
     AsmJSCoercion coercion;
     ParseNode *coercedExpr;
     if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
         return false;
     return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst);
 }
 
 static bool
+IsArrayViewCtorName(ModuleCompiler &m, PropertyName *name, Scalar::Type *type)
+{
+    JSAtomState &names = m.cx()->names();
+    if (name == names.Int8Array || name == names.SharedInt8Array)
+        *type = Scalar::Int8;
+    else if (name == names.Uint8Array || name == names.SharedUint8Array)
+        *type = Scalar::Uint8;
+    else if (name == names.Int16Array || name == names.SharedInt16Array)
+        *type = Scalar::Int16;
+    else if (name == names.Uint16Array || name == names.SharedUint16Array)
+        *type = Scalar::Uint16;
+    else if (name == names.Int32Array || name == names.SharedInt32Array)
+        *type = Scalar::Int32;
+    else if (name == names.Uint32Array || name == names.SharedUint32Array)
+        *type = Scalar::Uint32;
+    else if (name == names.Float32Array || name == names.SharedFloat32Array)
+        *type = Scalar::Float32;
+    else if (name == names.Float64Array || name == names.SharedFloat64Array)
+        *type = Scalar::Float64;
+    else
+        return false;
+    return true;
+}
+
+static bool
 CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr)
 {
-    ParseNode *ctorExpr = ListHead(newExpr);
-    if (!ctorExpr->isKind(PNK_DOT))
-        return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'");
-
-    ParseNode *base = DotBase(ctorExpr);
-    PropertyName *field = DotMember(ctorExpr);
-
     PropertyName *globalName = m.module().globalArgumentName();
     if (!globalName)
-        return m.fail(base, "cannot create array view without an asm.js global parameter");
-    if (!IsUseOfName(base, globalName))
-        return m.failName(base, "expecting '%s.*Array", globalName);
+        return m.fail(newExpr, "cannot create array view without an asm.js global parameter");
+
+    PropertyName *bufferName = m.module().bufferArgumentName();
+    if (!bufferName)
+        return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
+
+    ParseNode *ctorExpr = ListHead(newExpr);
+
+    PropertyName *field;
+    Scalar::Type type;
+    if (ctorExpr->isKind(PNK_DOT)) {
+        ParseNode *base = DotBase(ctorExpr);
+
+        if (!IsUseOfName(base, globalName))
+            return m.failName(base, "expecting '%s.*Array", globalName);
+
+        field = DotMember(ctorExpr);
+        if (!IsArrayViewCtorName(m, field, &type))
+            return m.fail(ctorExpr, "could not match typed array name");
+    } else {
+        if (!ctorExpr->isKind(PNK_NAME))
+            return m.fail(ctorExpr, "expecting name of imported array view constructor");
+
+        PropertyName *globalName = ctorExpr->name();
+        const ModuleCompiler::Global *global = m.lookupGlobal(globalName);
+        if (!global)
+            return m.failName(ctorExpr, "%s not found in module global scope", globalName);
+
+        if (global->which() != ModuleCompiler::Global::ArrayViewCtor)
+            return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName);
+
+        field = nullptr;
+        type = global->viewType();
+    }
 
     ParseNode *bufArg = NextNode(ctorExpr);
     if (!bufArg || NextNode(bufArg) != nullptr)
         return m.fail(ctorExpr, "array view constructor takes exactly one argument");
 
-    PropertyName *bufferName = m.module().bufferArgumentName();
-    if (!bufferName)
-        return m.fail(bufArg, "cannot create array view without an asm.js heap parameter");
     if (!IsUseOfName(bufArg, bufferName))
         return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
 
-    JSAtomState &names = m.cx()->names();
-    Scalar::Type type;
-    if (field == names.Int8Array || field == names.SharedInt8Array)
-        type = Scalar::Int8;
-    else if (field == names.Uint8Array || field == names.SharedUint8Array)
-        type = Scalar::Uint8;
-    else if (field == names.Int16Array || field == names.SharedInt16Array)
-        type = Scalar::Int16;
-    else if (field == names.Uint16Array || field == names.SharedUint16Array)
-        type = Scalar::Uint16;
-    else if (field == names.Int32Array || field == names.SharedInt32Array)
-        type = Scalar::Int32;
-    else if (field == names.Uint32Array || field == names.SharedUint32Array)
-        type = Scalar::Uint32;
-    else if (field == names.Float32Array || field == names.SharedFloat32Array)
-        type = Scalar::Float32;
-    else if (field == names.Float64Array || field == names.SharedFloat64Array)
-        type = Scalar::Float64;
-    else
-        return m.fail(ctorExpr, "could not match typed array name");
-
     return m.addArrayView(varName, type, field);
 }
 
 static bool
 IsSimdTypeName(ModuleCompiler &m, PropertyName *name, AsmJSSimdType *type)
 {
     if (name == m.cx()->names().int32x4) {
         *type = AsmJSSimdType_int32x4;
@@ -3659,17 +3694,22 @@ CheckGlobalDotImport(ModuleCompiler &m, 
     if (!base->isKind(PNK_NAME))
         return m.fail(base, "expected name of variable or parameter");
 
     if (base->name() == m.module().globalArgumentName()) {
         if (field == m.cx()->names().NaN)
             return m.addGlobalConstant(varName, GenericNaN(), field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
-        return m.failName(initNode, "'%s' is not a standard global constant", field);
+
+        Scalar::Type type;
+        if (IsArrayViewCtorName(m, field, &type))
+            return m.addArrayViewCtor(varName, type, field);
+
+        return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
     }
 
     if (base->name() == m.module().importArgumentName())
         return m.addFFI(varName, field);
 
     const ModuleCompiler::Global *global = m.lookupGlobal(base->name());
     if (!global)
         return m.failName(initNode, "%s not found in module global scope", base->name());
@@ -3925,16 +3965,17 @@ CheckVarRef(FunctionCompiler &f, ParseNo
             *def = f.loadGlobalVar(*global);
             *type = global->varOrConstType().toType();
             break;
           case ModuleCompiler::Global::Function:
           case ModuleCompiler::Global::FFI:
           case ModuleCompiler::Global::MathBuiltinFunction:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
+          case ModuleCompiler::Global::ArrayViewCtor:
           case ModuleCompiler::Global::SimdCtor:
           case ModuleCompiler::Global::SimdOperation:
             return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
@@ -5181,16 +5222,17 @@ CheckCoercedCall(FunctionCompiler &f, Pa
             return CheckFFICall(f, call, global->ffiIndex(), retType, def, type);
           case ModuleCompiler::Global::MathBuiltinFunction:
             return CheckCoercedMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type);
           case ModuleCompiler::Global::ConstantLiteral:
           case ModuleCompiler::Global::ConstantImport:
           case ModuleCompiler::Global::Variable:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
+          case ModuleCompiler::Global::ArrayViewCtor:
             return f.failName(callee, "'%s' is not callable function", callee->name());
           case ModuleCompiler::Global::SimdCtor:
           case ModuleCompiler::Global::SimdOperation:
             return CheckCoercedSimdCall(f, call, global, retType, def, type);
           case ModuleCompiler::Global::Function:
             break;
         }
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testResize.js
@@ -0,0 +1,32 @@
+load(libdir + "asm.js");
+
+assertAsmTypeFail('glob', USE_ASM + "var I32=glob.Int32Arra; function f() {} return f");
+var m = asmCompile('glob', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
+assertAsmLinkFail(m, {});
+assertAsmLinkFail(m, {Int32Array:null});
+assertAsmLinkFail(m, {Int32Array:{}});
+assertAsmLinkFail(m, {Int32Array:Uint32Array});
+assertEq(asmLink(m, {Int32Array:Int32Array})(), undefined);
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
+assertEq(asmLink(m, this)(), undefined);
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
+
+assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I3(buf); function f() {} return f');
+assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=0; var i32=new I32(buf); function f() {} return f');
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I32(buf); function f() {} return f');
+assertAsmLinkFail(m, this, null, {});
+assertAsmLinkAlwaysFail(m, this, null, null);
+assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
+
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
+assertAsmLinkFail(m, this, null, {});
+assertAsmLinkAlwaysFail(m, this, null, null);
+assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
+
+var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var F32=glob.Float32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
+assertAsmLinkFail(m, this, null, {});
+assertAsmLinkAlwaysFail(m, this, null, null);
+assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
+assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);